diff --git a/.gitignore b/.gitignore index 936d613..5e62d79 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ bootstrap_capg/go.mod bootstrap_capz/.vscode/launch.json bootstrap_capz/go.mod bootstrap_capz/go.sum +bootstrap_capo/capo-ephemeral diff --git a/bootstrap_capo/Dockerfile b/bootstrap_capo/Dockerfile new file mode 100644 index 0000000..1ff6945 --- /dev/null +++ b/bootstrap_capo/Dockerfile @@ -0,0 +1,66 @@ +# Copyright 2018 AT&T Intellectual Property. All other rights reserved. +# +# 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. + +ARG GO_IMAGE=golang:1.14.4 + +ARG PYTHON_IMAGE=python:3.8 + +FROM ${GO_IMAGE} as builder + +ENV GO111MODULE=on \ + CGO_ENABLED=0 \ + GOOS=linux \ + GOARCH=amd64 + +WORKDIR /home/build + +COPY main.go go.mod go.sum ./ +COPY config/ config/ +COPY resource/ resource/ + +RUN go install /home/build +RUN go build -o capo-ephemeral /home/build + +FROM ${PYTHON_IMAGE} + +LABEL org.opencontainers.image.authors='airship-discuss@lists.airshipit.org, irc://#airshipit@freenode' \ + org.opencontainers.image.url='https://airshipit.org' \ + org.opencontainers.image.source='https://opendev.org/airship/images' \ + org.opencontainers.image.vendor='The Airship Authors' \ + org.opencontainers.image.licenses='Apache-2.0' + +RUN set -ex && \ + pip install python-openstackclient + +RUN set -ex && \ + apt-get update + +RUN useradd -m bootstrap + +USER bootstrap + +WORKDIR /home/bootstrap + +ENV HOME=/home/bootstrap + +ENV PATH="${PATH}:${HOME}" + +# Copy the binary from builder +COPY --from=builder /home/build/capo-ephemeral . + +# Copy resources including scripts and help.txt file +COPY resource/* $HOME/ + +# Executes the go application capo-ephemeral +CMD ["capo-ephemeral"] \ No newline at end of file diff --git a/bootstrap_capo/Makefile b/bootstrap_capo/Makefile new file mode 100644 index 0000000..9ecdb64 --- /dev/null +++ b/bootstrap_capo/Makefile @@ -0,0 +1,55 @@ +SHELL := /bin/bash + +PUSH_IMAGE ?= false + +DOCKER_MAKE_TARGET := build + +# docker image options +DOCKER_REGISTRY ?= quay.io +DOCKER_FORCE_CLEAN ?= true +DOCKER_IMAGE_NAME ?= capo-ephemeral +DOCKER_IMAGE_PREFIX ?= airshipit +DOCKER_IMAGE_TAG ?= latest +DOCKER_IMAGE ?= $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_PREFIX)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG) +DOCKER_TARGET_STAGE ?= release +CONTAINER_TEMP = capo-ephemeral-temp + +.PHONY: all +all: build docker + +.PHONY: images +images: build docker + +.PHONY: build +build: main.go go.mod go.sum config/openstack_cluster.go config/openstack_config.go Dockerfile + @docker build --target builder --network=host \ + --build-arg MAKE_TARGET=$(DOCKER_MAKE_TARGET) \ + --tag $(DOCKER_IMAGE) . + docker run --name $(CONTAINER_TEMP) $(DOCKER_IMAGE) /bin/true + docker cp $(CONTAINER_TEMP):/home/build/capo-ephemeral . + +.PHONY: docker +docker: capo-ephemeral resource/create-k8s-cluster.sh resource/delete-k8s-cluster.sh resource/user-data.sh resource/help.txt Dockerfile + @docker build . --network=host \ + --build-arg MAKE_TARGET=$(DOCKER_MAKE_TARGET) \ + --tag $(DOCKER_IMAGE) \ + --force-rm=$(DOCKER_FORCE_CLEAN) + +ifeq ($(PUSH_IMAGE), true) + docker push $(DOCKER_IMAGE) +endif + +.PHONY: clean +clean: + @rm capo-ephemeral + +.PHONY: lint +lint: + @echo TODO + +# style checks +.PHONY: tests +tests: images + sudo rm -f $(HELP_FILE) + cp openstack-config.yaml /tmp + docker run -v /tmp:/kube --env-file bootstrap-env.list --name capo-bootstrap-test $(DOCKER_IMAGE) \ No newline at end of file diff --git a/bootstrap_capo/README.md b/bootstrap_capo/README.md new file mode 100644 index 0000000..e502d06 --- /dev/null +++ b/bootstrap_capo/README.md @@ -0,0 +1,102 @@ +# Openstack Bootstrap Container + +This project contains the Go application as well as the shell scripts and configuration files for +implementing the Openstack Bootstrap container. + +The Openstack Bootstrap container is responsible to create or delete a Kubernetes (K8S) cluster on +Openstack. The K8S cluster is created using `kubeadm`. + +## Go Application + +The Go application is the container orchestrator that is responsible for translating commands +into actions: create, delete, help. + +The Go application reads the Ephemeral cluster configuration file (e.g., openstack-config.yaml) and +converts the attributes into environment variables. These environment variables are used by the +shell scripts to create or delete the K8S cluster. + +To build this Go application, execute the following commands: + +```bash +go install . +go build -o capo-ephemeral +``` + +## Shell Scripts + +The shell scripts make use of openstack cli commands to create and delete K8S cluster. +The other alternative that was considered was to use magnum container orchestration APIs. +In order to keep things generic, openstack cli command was chosen to create and delete the K8S +cluster. + +### Create K8S Cluster script + +The **create-k8s-cluster.sh** script creates a K8S cluster using the information provided in +the Ephemeral cluster configuration file. It passes `user-data.sh` script in the +`openstack server create` command to execute series of steps to initiate creation of the K8S +cluster at boot time. Once the cluster is created, its **kubeconfig** file is copied to the +container's volume mount, "sharing" it with the host. + +### Delete K8S Cluster script + +The **delete-k8s-cluster.sh** script deletes the underlying VM and the K8S cluster using the +information provided in the Ephemeral cluster configuration file. + +## Dockerfile + +The **Dockerfile** is used to build the Openstack Bootstrap container image. +Execute the following command to build the Bootstrap container image: + +```bash +make docker +``` + +## Pre-requisite + +- [Devstack](https://docs.openstack.org/devstack/latest/guides/devstack-with-lbaas-v2.html) +is installed. +- The most recent version of the *64-bit amd64-arch QCOW2* image for *Ubuntu 18.04* is used for +creating the ephemeral cluster. + The image is available + [here](https://cloud-images.ubuntu.com/bionic/current/bionic-server-cloudimg-amd64.img) for + download and should be available in the devstack environment. + +- `~/.airship/` directory on host machine contains `clouds.yaml` and `openstack-config.yaml` files. +- airship configuration file is updated with `ephemeral` cluster configuration information. + +## Appendix + +### Required Configuration + +#### Ephemeral Cluster Configuration + +```bash +$ cat openstack-config.yaml +apiVersion: v1 +kind: OpenstackCloudConfig +metadata: + name: capi-openstack +credentials: + credential: clouds.yaml + cloudName: devstack +spec: + cluster: + machineSize: ds4G + kubeconfig: capo.kubeconfig + securityGroup: bootstrap-sec-grp +``` + +#### Airship Configuration + +```bash +$ cat ~/.airship/config +apiVersion: airshipit.org/v1alpha1 +bootstrapInfo: + ephemeral: + container: + image: quay.io/airshipit/capo-bootstrap:latest + name: capo-bootstrap + volume: /home/stack/.airship:/kube + ephemeralCluster: + config: openstack-config.yaml +``` diff --git a/bootstrap_capo/bootstrap-env.list b/bootstrap_capo/bootstrap-env.list new file mode 100644 index 0000000..f5cd13f --- /dev/null +++ b/bootstrap_capo/bootstrap-env.list @@ -0,0 +1,3 @@ +BOOTSTRAP_COMMAND=help +BOOTSTRAP_CONFIG=openstack-config.yaml +BOOTSTRAP_VOLUME=/tmp:/kube diff --git a/bootstrap_capo/clouds.yaml b/bootstrap_capo/clouds.yaml new file mode 100644 index 0000000..6d884de --- /dev/null +++ b/bootstrap_capo/clouds.yaml @@ -0,0 +1,13 @@ +clouds: + devstack: + auth: + auth_url: "http://10.0.1.4/identity" + password: pass + project_domain_id: default + project_name: demo + user_domain_id: default + username: demo + identity_api_version: "3" + region_name: RegionOne + volume_api_version: "3" + diff --git a/bootstrap_capo/config/openstack_cluster.go b/bootstrap_capo/config/openstack_cluster.go new file mode 100644 index 0000000..ece01d5 --- /dev/null +++ b/bootstrap_capo/config/openstack_cluster.go @@ -0,0 +1,104 @@ +/* + 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 + + https://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 config + +import ( + "errors" + "fmt" + "io/ioutil" + "log" + "os" + "os/exec" + "path/filepath" + "strings" +) + +const ( + // Bootstrap container environment variables + openstackCredential = "OS_CREDENTIAL_FILE" + openstackSecurityGroup = "OS_SECURITY_GROUP" + openstackCloudName = "OS_CLOUD" + openstackMachineSize = "OS_MACHINE_FLAVOR" + openstackKubeconfigFile = "OS_KUBECONFIG_FILE" + + bootstrapHelpFile = "help.txt" + + // BootstrapCommand environment variable + bootstrapHome = "SRC_DIR" + bootstrapVolumeSep = ":" +) + +// SetOpenstackCloudEnvVars sets the environment variables used by the script +func SetOpenstackCloudEnvVars(config *OpenstackConfig) error { + err := os.Setenv(openstackCredential, config.Credentials.Credential) + err = os.Setenv(openstackCloudName, config.Credentials.CloudName) + err = os.Setenv(openstackSecurityGroup, config.Spec.Cluster.SecurityGroup) + err = os.Setenv(openstackMachineSize, config.Spec.Cluster.MachineSize) + err = os.Setenv(openstackKubeconfigFile, config.Spec.Cluster.Kubeconfig) + + if err != nil { + return err + } + return nil +} + +// GetVolumeMountPoints extracts the source and destination of a volume mount +func GetVolumeMountPoints(volumeMount string) (string, string, error) { + if len(volumeMount) == 0 { + return "", "", errors.New("volume mount is mandatory, please provide volume mount") + } + sepPos := strings.Index(volumeMount, bootstrapVolumeSep) + srcMountPoint := volumeMount[:sepPos] + dstMountPoint := volumeMount[sepPos+1:] + + return srcMountPoint, dstMountPoint, nil +} + +// CreateOSCluster creates the ephemeral K8S cluster in Openstack +func CreateOSCluster() error { + srcDir := os.Getenv(bootstrapHome) + shellScriptFile := "./create-k8s-cluster.sh" + shellScript := filepath.Join(srcDir, shellScriptFile) + return execute(shellScript) +} + +// DeleteOSCluster deletes the ephemeral K8S cluster in Openstack +func DeleteOSCluster() error { + srcDir := os.Getenv(bootstrapHome) + shellScriptFile := "./delete-k8s-cluster.sh" + shellScript := filepath.Join(srcDir, shellScriptFile) + return execute(shellScript) +} + +func execute(shellScript string) error { + cmd := exec.Command(shellScript) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + log.Printf("Executing script %s\n", shellScript) + if err := cmd.Start(); err != nil { + return err + } + return cmd.Wait() +} + +// HelpOSCluster prints the help information to supplement the creation of K8S ephemeral cluster +func HelpOSCluster() error { + srcDir := os.Getenv(bootstrapHome) + src := filepath.Join(srcDir, bootstrapHelpFile) + b, err := ioutil.ReadFile(src) + fmt.Fprintln(os.Stdout, string(b)) + return err +} diff --git a/bootstrap_capo/config/openstack_config.go b/bootstrap_capo/config/openstack_config.go new file mode 100644 index 0000000..a3a08bf --- /dev/null +++ b/bootstrap_capo/config/openstack_config.go @@ -0,0 +1,82 @@ +/* +Copyright 2014 The Kubernetes 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 config + +import ( + "io/ioutil" + "log" + + "sigs.k8s.io/yaml" +) + +// OpenstackConfig holds configurations for bootstrap steps +type OpenstackConfig struct { + // +optional + Kind string `yaml:"kind,omitempty"` + + // +optional + APIVersion string `yaml:"apiVersion,omitempty"` + + // Configuration parameters for metadata + Metadata *Metadata `yaml:"metadata"` + + // Configuration parameters for metadata + Credentials *Credentials `yaml:"credentials"` + + // Configuration parameters for spec + Spec *Spec `yaml:"spec"` +} + +// Metadata structure provides the cluster name to assign and labels to the k8s cluster +type Metadata struct { + Name string `yaml:"name"` + Labels []string `yaml:"labels,omitempty"` +} + +// Credentials structu provides the credentials to authenticate with Azure Cloud +type Credentials struct { + Credential string `yaml:"credential"` + CloudName string `yaml:"cloudName"` +} + +// Spec structure contains the info for the ck8s luster to deploy +type Spec struct { + Cluster Cluster `yaml:"cluster"` +} + +// Cluster struct provides data for the k8s cluster to deploy +type Cluster struct { + // flavor of VM + MachineSize string `yaml:"machineSize,omitempty"` + + // Kubeconfig filename to save + Kubeconfig string `yaml:"kubeconfig,omitempty"` + + // security group + SecurityGroup string `yaml:"securityGroup,omitempty"` +} + +// ReadYAMLFile reads YAML-formatted configuration file and +// de-serializes it to a given object +func ReadYAMLFile(filePath string, cfg *OpenstackConfig) error { + data, err := ioutil.ReadFile(filePath) + log.Printf("Attempting to read Openstack bootstrap cluster configuration file at '%s'", filePath) + if err != nil { + return err + } + return yaml.Unmarshal(data, cfg) +} diff --git a/bootstrap_capo/go.mod b/bootstrap_capo/go.mod new file mode 100644 index 0000000..1b9d6ed --- /dev/null +++ b/bootstrap_capo/go.mod @@ -0,0 +1,8 @@ +module capo + +go 1.14 + +require ( + github.com/pkg/errors v0.9.1 + sigs.k8s.io/yaml v1.2.0 +) diff --git a/bootstrap_capo/go.sum b/bootstrap_capo/go.sum new file mode 100644 index 0000000..057f69b --- /dev/null +++ b/bootstrap_capo/go.sum @@ -0,0 +1,8 @@ +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/bootstrap_capo/main.go b/bootstrap_capo/main.go new file mode 100644 index 0000000..69912b0 --- /dev/null +++ b/bootstrap_capo/main.go @@ -0,0 +1,85 @@ +/* + 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 + + https://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 main + +import ( + "capo/config" + "log" + "os" + + "flag" +) + +const ( + createCmd = "create" + deleteCmd = "delete" + helpCmd = "help" +) + +func main() { + var configPath string + + flag.StringVar(&configPath, "c", "", "Path for the Openstack Cloud bootstrap configuration (yaml) file") + flag.Parse() + + volMount := os.Getenv("BOOTSTRAP_VOLUME") + + _, dstMount, err := config.GetVolumeMountPoints(volMount) + + if err != nil { + log.Printf("Failed to get volume mount, please provide volume mount") + os.Exit(1) + } + openstackConfigPath := dstMount + "/" + os.Getenv("BOOTSTRAP_CONFIG") + if len(configPath) == 0 { + configPath = openstackConfigPath + } + + configYAML := config.OpenstackConfig{} + err = config.ReadYAMLFile(configPath, &configYAML) + if err != nil { + log.Printf("Failed to load Openstack Cloud Bootstrap config file") + os.Exit(2) + } + + err = config.SetOpenstackCloudEnvVars(&configYAML) + if err != nil { + log.Printf("Failed to set Openstack Cloud environment variables") + os.Exit(3) + } + + command := os.Getenv("BOOTSTRAP_COMMAND") + switch { + case command == createCmd: + err = config.CreateOSCluster() + if err != nil { + os.Exit(4) + } + case command == deleteCmd: + err = config.DeleteOSCluster() + if err != nil { + os.Exit(5) + } + case command == helpCmd: + err = config.HelpOSCluster() + if err != nil { + os.Exit(6) + } + default: + log.Printf("The --command parameter value shall be 'create', 'delete' or 'help'") + os.Exit(7) + } + os.Exit(0) +} diff --git a/bootstrap_capo/openstack-config.yaml b/bootstrap_capo/openstack-config.yaml new file mode 100644 index 0000000..1c42b27 --- /dev/null +++ b/bootstrap_capo/openstack-config.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: OpenstackCloudConfig +metadata: + name: capi-openstack +credentials: + credential: clouds.yaml + cloudName: devstack +spec: + cluster: + machineSize: ds4G + kubeconfig: capo.kubeconfig + securityGroup: bootstrap-sec-grp diff --git a/bootstrap_capo/resource/create-k8s-cluster.sh b/bootstrap_capo/resource/create-k8s-cluster.sh new file mode 100755 index 0000000..fcdd70b --- /dev/null +++ b/bootstrap_capo/resource/create-k8s-cluster.sh @@ -0,0 +1,144 @@ +#!/bin/bash + +# 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. + + +# Bootstrap Environment Variables MUST be provided when running the Container +echo "Checking that Openstack Cloud Name has been provided ..." +if [[ -z "$OS_CLOUD" ]]; then + echo "Openstack cloud name MUST be provided." + exit 1 +else + echo "OS_CLOUD = $OS_CLOUD" +fi + +echo "Checking that Openstack Cloud Configuration has been provided ..." +if [[ -z "$OS_CREDENTIAL_FILE" ]]; then + echo "Openstack Cloud Configuration MUST be provided." + exit 1 +else + echo "OS_CREDENTIAL_FILE = $OS_CREDENTIAL_FILE" +fi + +echo "" +echo "Checking Environment Variables used by the Bootstrap Container ..." + +if [[ -z "$OS_MACHINE_FLAVOR" ]]; then + echo "Assigning default value for OS_MACHINE_FLAVOR" + export OS_MACHINE_FLAVOR="ds2G" +fi + +if [[ -z "$OS_KUBECONFIG_FILE" ]]; then + echo "Assigning default value for OS_KUBECONFIG_FILE" + export OS_KUBECONFIG_FILE="kubeconfig" +fi +if [[ -z "$OS_SECURITY_GROUP" ]]; then + echo "Assigning default value for OS_SECURITY_GROUP" + export OS_SECURITY_GROUP="bootstrap-mgmt-sec-grp" +fi + +cp /kube/"$OS_CREDENTIAL_FILE" ~ + +echo "OS_CLOUD = $OS_CLOUD" +echo "OS_CREDENTIAL_FILE = $OS_CREDENTIAL_FILE" +echo "OS_MACHINE_FLAVOR = $OS_MACHINE_FLAVOR" +echo "OS_KUBECONFIG_FILE = $OS_KUBECONFIG_FILE" +echo "OS_SECURITY_GROUP = $OS_SECURITY_GROUP" +echo "" +echo "" + + +echo "creating envs" + +export SECURITY_GROUP=$OS_SECURITY_GROUP +export CAPI_VM="bootstrap-k8s" + +export OS_USERNAME=admin + +echo "listing all active images" +openstack image list + +echo "SECURITY_GROUP = $SECURITY_GROUP" +echo "VM NAME = $CAPI_VM" + +#echo "creating security group" +openstack security group create --project demo --project-domain Default $SECURITY_GROUP + +#echo "adding rules to the security group" +openstack security group rule create $SECURITY_GROUP --protocol tcp --remote-ip 0.0.0.0/0 + +openstack security group rule create $SECURITY_GROUP --protocol tcp --dst-port 10248:10252 --remote-ip 0.0.0.0/0 + +export PRIVATE_NETWORK_ID=$(openstack network show private | grep "\" | awk '{print $4}' ) +export K8S_IMAGE_ID=$(openstack image list | grep "ubuntu-k8s" | awk '{print $2}' ) + +echo "PRIVATE_NW_ID = $PRIVATE_NETWORK_ID" +echo "K8S_IMAGE = $K8S_IMAGE_ID" + +#Generate ssh key pair without being prompted for pass phrase +ssh-keygen -f ~/.ssh/id_rsa -t rsa -N '' + +echo "printing public key" +echo $(cat ~/.ssh/id_rsa.pub) +export SSH_KEY_PUB=$(cat ~/.ssh/id_rsa.pub) +echo $SSH_KEY_PUB > stack.pub + +openstack keypair delete stack + +echo "creating openstack key pair" +openstack keypair create --public-key stack.pub stack + +echo "************** listing key pairs ***************" +openstack keypair list + +export FLOATING_IP_ADDRESS=${FLOATING_IP_ADDRESS:-172.24.4.199} + +echo "Add floating IP to public network" +openstack floating ip create public --floating-ip-address $FLOATING_IP_ADDRESS + +echo "creating vm for spinning up ephemeral kubernetes cluster" +openstack server create --image $K8S_IMAGE_ID --flavor $OS_MACHINE_FLAVOR --security-group $SECURITY_GROUP --nic net-id=$PRIVATE_NETWORK_ID \ + --key-name stack --user-data user-data.sh $CAPI_VM --wait + +echo "associating floating ip with vm" +openstack server add floating ip $CAPI_VM $FLOATING_IP_ADDRESS + +echo "waiting for kubernets cluster to be up" + +#echo "check if kube config is ready on remote vm" + +N=0 +MAX_RETRY=30 +DELAY=60 + +until [ "$N" -ge ${MAX_RETRY} ] +do + if ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_rsa ubuntu@$FLOATING_IP_ADDRESS '[ -d /home/ubuntu/.kube/ ]'; then + printf "kube config is available\n" + break + else + printf "Kube config does not exist, or still being created\n" + N=$((N+1)) + echo "$N: Retry to check if kubeconfig exists" + sleep ${DELAY} + fi +done + +echo "copying the kubeconfig of ephemeral cluster to container host" +scp -o StrictHostKeyChecking=no -i ~/.ssh/id_rsa ubuntu@$FLOATING_IP_ADDRESS:/home/ubuntu/.kube/config /kube/$OS_KUBECONFIG_FILE + +chmod +rw /kube/$OS_KUBECONFIG_FILE + +echo "done copying kubeconfig file" + +echo "*************** done ***************" \ No newline at end of file diff --git a/bootstrap_capo/resource/delete-k8s-cluster.sh b/bootstrap_capo/resource/delete-k8s-cluster.sh new file mode 100755 index 0000000..1da30ae --- /dev/null +++ b/bootstrap_capo/resource/delete-k8s-cluster.sh @@ -0,0 +1,80 @@ +#!/bin/bash + +# 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. + +# Bootstrap Environment Variables MUST be provided when running the Container +echo "Checking that Openstack Cloud Name has been provided ..." +if [[ -z "$OS_CLOUD" ]]; then + echo "Openstack cloud name MUST be provided." + exit 1 +else + echo "OS_CLOUD = $OS_CLOUD" +fi + +echo "Checking that Openstack Cloud Configuration has been provided ..." +if [[ -z "$OS_CREDENTIAL_FILE" ]]; then + echo "Openstack Cloud Configuration MUST be provided." + exit 1 +else + echo "OS_CREDENTIAL_FILE = $OS_CREDENTIAL_FILE" +fi + +cp /kube/$OS_CREDENTIAL_FILE ~ + +echo "deleting the k8s ephemeral cluster" + +if [[ -z "$OS_KUBECONFIG_FILE" ]]; then + echo "Assigning default value for OS_KUBECONFIG_FILE" + export OS_KUBECONFIG_FILE="kubeconfig" +fi +if [[ -z "$OS_SECURITY_GROUP" ]]; then + echo "Assigning default value for OS_SECURITY_GROUP" + export OS_SECURITY_GROUP="bootstrap-mgmt-sec-grp" +fi + +export SECURITY_GROUP=$OS_SECURITY_GROUP +export CAPI_VM="bootstrap-k8s" +export FLOATING_IP_ADDRESS=${FLOATING_IP_ADDRESS:-172.24.4.199} +echo "OS_CLOUD = $OS_CLOUD" +echo "OS_CREDENTIAL_FILE = $OS_CREDENTIAL_FILE" +echo "OS_MACHINE_FLAVOR = $OS_MACHINE_FLAVOR" +echo "OS_KUBECONFIG_FILE = $OS_KUBECONFIG_FILE" +echo "OS_SECURITY_GROUP = $OS_SECURITY_GROUP" +echo "" +echo "" + +openstack server delete $CAPI_VM --wait + +if [ $? -ne 0 ]; then + echo "*** Failed to delete cluster in VM $CAPI_VM " + exit 1 +fi + +echo "K8s cluster deleted successfully in VM $CAPI_VM" + +openstack security group delete $SECURITY_GROUP + +if [ $? -ne 0 ]; then + echo "*** Failed to delete security group $SECURITY_GROUP." + exit 1 +fi +echo "Security Group $SECURITY_GROUP is deleted successfully." + +openstack floating ip delete $FLOATING_IP_ADDRESS +if [ $? -ne 0 ]; then + echo "*** Failed to delete floating ip $FLOATING_IP_ADDRESS." + exit 1 +fi +echo "Floating IP $FLOATING_IP is released successfully." + +echo "Kubernetes ephemeral cluster on vm $CAPI_VM is deleted successfully." diff --git a/bootstrap_capo/resource/help.txt b/bootstrap_capo/resource/help.txt new file mode 100644 index 0000000..bf61590 --- /dev/null +++ b/bootstrap_capo/resource/help.txt @@ -0,0 +1,32 @@ +Openstack Ephemeral Configuration File Definition +----------------------------------------------------- +The Openstack Bootstrap container creates an Ephemeral K8S cluster on the Openstack. +The container requires clouds.yaml credentials and other information about the cluster to deploy. +It requires a YAML configuration file with the format provided below. + + +apiVersion: v1 +kind: OpenstackConfig +metadata: + name: +credentials: + credential: + cloudName: +spec: + cluster: + machineSize: + kubeconfig: + securityGroup: + + +The expected location for the Openstack bootstrap configuration file is dictated by the "volume" mount +specified in the Airship config file (bootstrapInfo.ephemeral.container.volume). +For example, $HOME/.airship folder and shown in the snippet below: + + +apiVersion: airshipit.org/v1alpha1 +bootstrapInfo: + ephemeral: + container: + volume: /home/stack/.airship:/kube + diff --git a/bootstrap_capo/resource/user-data.sh b/bootstrap_capo/resource/user-data.sh new file mode 100755 index 0000000..cca7d45 --- /dev/null +++ b/bootstrap_capo/resource/user-data.sh @@ -0,0 +1,55 @@ +#!/bin/sh + +DEBIAN_FRONTEND=noninteractive apt-get remove -y resolvconf +sed -i 's/domain-name-servers, domain-search, //' /etc/dhcp/dhclient.conf +service networking restart +sed -i '/nameserver/d' /etc/resolv.conf +export NAMESERVER=${NAMESERVER:-8.8.8.8} +export NAMESERVER_OTHER=${NAMESERVER_OTHER:-8.8.4.4} +echo 'nameserver '"$NAMESERVER" >> /etc/resolv.conf +echo 'nameserver '"$NAMESERVER_OTHER" >> /etc/resolv.conf +echo "nameserver is set" + +apt-get update +apt-get upgrade -y +apt-get install -y apt-transport-https ca-certificates curl \ + software-properties-common + +apt-get install -y docker.io + +bash -c 'cat << EOF > /etc/docker/daemon.json +{ + "exec-opts": ["native.cgroupdriver=systemd"] +} +EOF' + +export APT_KEY_GPG_URL=${APT_KEY_GPG_URL:-https://packages.cloud.google.com/apt/doc/apt-key.gpg} +export K8S_IO_DEB_URL=${K8S_IO_DEB_URL:-http://apt.kubernetes.io/} + +curl -s "$APT_KEY_GPG_URL" | sudo apt-key add - + +bash -c 'cat << EOF > /etc/apt/sources.list.d/kubernetes.list +deb $K8S_IO_DEB_URL kubernetes-xenial main +EOF' + +export K8S_VERSION=${K8S_VERSION:-1.19.0-00} + +apt update + +apt install -y kubelet="$K8S_VERSION" kubeadm="$K8S_VERSION" kubectl="$K8S_VERSION" + +export CONTROL_PLANE_IP=${CONTROL_PLANE_IP:-172.24.4.199} + +kubeadm init --pod-network-cidr=10.244.0.0/16 --apiserver-cert-extra-sans="$CONTROL_PLANE_IP" \ + --control-plane-endpoint="$CONTROL_PLANE_IP" + +mkdir -p /home/ubuntu/.kube + +cp -i /etc/kubernetes/admin.conf /home/ubuntu/.kube/config + +chown ubuntu:ubuntu /home/ubuntu/.kube/config + +export CALICO_URL=${CALICO_URL:-https://docs.projectcalico.org/v3.15/manifests/calico.yaml} + +export KUBECONFIG=/etc/kubernetes/admin.conf && \ +kubectl apply -f "$CALICO_URL" \ No newline at end of file diff --git a/playbooks/airship-images-go-install.yaml b/playbooks/airship-images-go-install.yaml new file mode 100644 index 0000000..490e527 --- /dev/null +++ b/playbooks/airship-images-go-install.yaml @@ -0,0 +1,28 @@ +# Copyright 2018 AT&T Intellectual Property. All other rights reserved. +# +# 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. + +- hosts: primary + vars_files: + - vars/version.yaml + tasks: + - name: install go language + shell: | + sudo -E curl -sO "{{url}}"/go/"{{golang_version}}".tar.gz + sudo tar -C /usr/local -xzf "{{golang_version}}".tar.gz + rm "{{golang_version}}".tar.gz + cat >> ~/.profile << EOF + export PATH=$PATH:/usr/local/go/bin + EOF + become: yes + when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu' diff --git a/playbooks/vars/version.yaml b/playbooks/vars/version.yaml new file mode 100644 index 0000000..6e40590 --- /dev/null +++ b/playbooks/vars/version.yaml @@ -0,0 +1,3 @@ +--- +golang_version: go1.14.1.linux-amd64 +url: https://dl.google.com \ No newline at end of file