Bootstrap container for Azure
This commit provides the Go code and scripts for the Bootstrap container for Azure. The Bootstrap container (bootstrap_capz) for Azure is designed to accept three commands: create, delete and help. - create - will create an Ephemeral AKS cluster in Azure Cloud - delete - will delete the Ephemeral AKS cluster from the Azure Cloud - help - Stdout the help text for using this container. Please, refer to the bootstrap_capz/README.md for further details. Change-Id: Id1947f7b831a5d6cf59296cf39ff2b436080483d
This commit is contained in:
parent
aa624ce27c
commit
58f39ffa95
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,3 +1,6 @@
|
|||||||
*build/
|
*build/
|
||||||
bootstrap_capg/.vscode/launch.json
|
bootstrap_capg/.vscode/launch.json
|
||||||
bootstrap_capg/go.mod
|
bootstrap_capg/go.mod
|
||||||
|
bootstrap_capz/.vscode/launch.json
|
||||||
|
bootstrap_capz/go.mod
|
||||||
|
bootstrap_capz/go.sum
|
||||||
|
57
bootstrap_capz/Dockerfile
Normal file
57
bootstrap_capz/Dockerfile
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
# 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 AZ_SDK=mcr.microsoft.com/azure-cli:2.8.0
|
||||||
|
ARG GOLANG=golang:1.14.4
|
||||||
|
|
||||||
|
############################################
|
||||||
|
# Build GCP Bootstrap Container application
|
||||||
|
############################################
|
||||||
|
FROM ${GOLANG} as builder
|
||||||
|
WORKDIR /home/build
|
||||||
|
# copy the capg bootstrap container app code
|
||||||
|
COPY main.go .
|
||||||
|
COPY config/ config/
|
||||||
|
# Build capg bootstrap container application
|
||||||
|
RUN go mod init opendev.org/airship/images/bootstrap_capz && \
|
||||||
|
go get -d -v ./... && \
|
||||||
|
go install . && \
|
||||||
|
CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o capz-ephemeral .
|
||||||
|
|
||||||
|
############################################
|
||||||
|
# Run Azure Bootstrap Container
|
||||||
|
############################################
|
||||||
|
FROM ${AZ_SDK}
|
||||||
|
|
||||||
|
LABEL org.opencontainers.image.authors='airship-discuss@lists.airshipit.org, irc://#airshipit@freenode' \
|
||||||
|
org.opencontainers.image.url='https://airshipit.org' \
|
||||||
|
org.opencontainers.image.documentation='https://opendev.org/airship/images/src/branch/master/bootstrap_capg/README.md' \
|
||||||
|
org.opencontainers.image.source='https://opendev.org/airship/images' \
|
||||||
|
org.opencontainers.image.vendor='The Airship Authors' \
|
||||||
|
org.opencontainers.image.licenses='Apache-2.0'
|
||||||
|
|
||||||
|
RUN adduser --disabled-password --gecos "" bootstrap
|
||||||
|
USER bootstrap
|
||||||
|
|
||||||
|
WORKDIR /home/bootstrap
|
||||||
|
ENV HOME=/home/bootstrap
|
||||||
|
ENV PATH="${PATH}:${HOME}"
|
||||||
|
|
||||||
|
# Copy the Azure Bootstrap Container command
|
||||||
|
COPY --from=builder /home/build/capz-ephemeral .
|
||||||
|
# Copy help file
|
||||||
|
COPY assets/help.txt .
|
||||||
|
|
||||||
|
# Executes the Azure Bootstrap command
|
||||||
|
CMD ["capz-ephemeral"]
|
56
bootstrap_capz/Makefile
Normal file
56
bootstrap_capz/Makefile
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
SHELL := /bin/bash
|
||||||
|
PUSH_IMAGE ?= false
|
||||||
|
GIT_VERSION ?= v0.1.0
|
||||||
|
GIT_MODULE ?= opendev.org/airship/airshipctl/pkg/version
|
||||||
|
|
||||||
|
GO_FLAGS := -ldflags '-extldflags "-static"' -tags=netgo
|
||||||
|
GO_FLAGS += -ldflags "-X ${GIT_MODULE}.gitVersion=${GIT_VERSION}"
|
||||||
|
|
||||||
|
DOCKER_MAKE_TARGET := build
|
||||||
|
|
||||||
|
# docker image options
|
||||||
|
DOCKER_REGISTRY ?= quay.io
|
||||||
|
DOCKER_FORCE_CLEAN ?= true
|
||||||
|
DOCKER_IMAGE_NAME ?= capz-bootstrap
|
||||||
|
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
|
||||||
|
|
||||||
|
PATH += :/usr/local/go/bin
|
||||||
|
HELP_FILE ?= /tmp/help.txt
|
||||||
|
ORIGINAL_HELP_FILE = ./assets/help.txt
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
|
all: images
|
||||||
|
|
||||||
|
.PHONY: images
|
||||||
|
images: Dockerfile \
|
||||||
|
main.go \
|
||||||
|
config/azure_cluster.go \
|
||||||
|
config/aks_cluster.go \
|
||||||
|
config/azure_config.go \
|
||||||
|
assets/help.txt
|
||||||
|
@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:
|
||||||
|
@docker image rm $(DOCKER_IMAGE)
|
||||||
|
|
||||||
|
.PHONY: lint
|
||||||
|
lint:
|
||||||
|
@echo TODO
|
||||||
|
|
||||||
|
# style checks
|
||||||
|
.PHONY: tests
|
||||||
|
tests: images
|
||||||
|
if [ -f $(HELP_FILE) ]; then sudo rm $(HELP_FILE); fi
|
||||||
|
cp azure-config.yaml /tmp
|
||||||
|
docker run -v /tmp:/kube --env-file bootstrap-env.list --name capz-test $(DOCKER_IMAGE)
|
||||||
|
cmp $(HELP_FILE) $(ORIGINAL_HELP_FILE)
|
32
bootstrap_capz/README.md
Normal file
32
bootstrap_capz/README.md
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# Azure Bootstrap Container
|
||||||
|
|
||||||
|
This project contains the Go application and configuration files for
|
||||||
|
implementing the Azure Bootstrap container.
|
||||||
|
|
||||||
|
The Azure Bootstrap container is responsible to create or delete a Kubernetes
|
||||||
|
(K8S) cluster on Azure Cloud platform using the AKS (Azure Kubernetes Service).
|
||||||
|
|
||||||
|
## Go Application
|
||||||
|
|
||||||
|
The Go application is the bootstrap container orchestrator that is responsible
|
||||||
|
for translating commands into actions: create, delete, help.
|
||||||
|
|
||||||
|
This Go application uses the Ephemeral cluster configuration file
|
||||||
|
(e.g., azure-config.yaml) to determine the Azure credentials and
|
||||||
|
data to use to create or delete the ephemeral cluster.
|
||||||
|
|
||||||
|
## Dockerfile
|
||||||
|
|
||||||
|
The **Dockerfile** uses a multi-stage builds to first build the Go application
|
||||||
|
then create the Azure bootstrap container image.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
To build the bootstrap container image, execute the following command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make images
|
||||||
|
```
|
||||||
|
|
||||||
|
This command will build the Go application and then create the bootstrap
|
||||||
|
container image.
|
64
bootstrap_capz/assets/help.txt
Normal file
64
bootstrap_capz/assets/help.txt
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
|
||||||
|
Azure Ephemeral Configuration File Definition
|
||||||
|
---------------------------------------------
|
||||||
|
The Azure Bootstrap container creates an Ephemeral K8S cluster on the Azure Cloud platform.
|
||||||
|
The container requires authentication credentials and other information about the cluster to deploy.
|
||||||
|
It requires a YAML configuration file with the format provided below.
|
||||||
|
|
||||||
|
<YAML>
|
||||||
|
apiVersion: v1
|
||||||
|
kind: AzureConfig
|
||||||
|
metadata:
|
||||||
|
name: <Ephemeral K8S cluster name>
|
||||||
|
credentials:
|
||||||
|
tenant: <Azure Subscription Tenant ID>
|
||||||
|
client: <Azure Subscription Service Principal ID>
|
||||||
|
secret: <Azure Subscription Service Principal Secret>
|
||||||
|
spec:
|
||||||
|
resourceGroup: <Azure Resource Group Name>
|
||||||
|
region: <Azure Region, e.g., centralus>
|
||||||
|
cluster:
|
||||||
|
k8sVersion: <Kubernetes version, e.g., 1.18.6>
|
||||||
|
vmSize: <Azure Compute VM Type, e.g., Standard_B2s>
|
||||||
|
replicas: <Node Replica Number for the cluster. Default is 1>
|
||||||
|
kubeconfig: <Kubernetes version, e.g., 1.18.2>
|
||||||
|
</YAML>
|
||||||
|
|
||||||
|
It also accepts the JSON file format.
|
||||||
|
|
||||||
|
<JSON>
|
||||||
|
{
|
||||||
|
"apiVersion":"v1",
|
||||||
|
"kind":"AzureConfig",
|
||||||
|
"metadata":{
|
||||||
|
"name":"<Ephemeral K8S cluster name>"
|
||||||
|
},
|
||||||
|
"credentials":{
|
||||||
|
"tenant":"<Azure Subscription Tenant ID>",
|
||||||
|
"client":"<Azure Subscription Service Principal ID>",
|
||||||
|
"secret":"<Azure Subscription Service Principal Secret>"
|
||||||
|
},
|
||||||
|
"spec":{
|
||||||
|
"resourceGroup":"<Azure Resource Group Name>",
|
||||||
|
"region":"<Azure Region, e.g., centralus>",
|
||||||
|
"cluster":{
|
||||||
|
"k8sVersion":"<Kubernetes version, e.g., 1.18.6>",
|
||||||
|
"vmSize":"<Azure Compute VM Type, e.g., Standard_B2s>",
|
||||||
|
"replicas":<Node Replica Number for the cluster>,
|
||||||
|
"kubeconfig":"<Kubernetes version, e.g., 1.18.2>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</JSON>
|
||||||
|
|
||||||
|
The expected location for the Azure bootstrap configuration file is dictated by the "volume" mount
|
||||||
|
specified in the Airship config file (bootstrapInfo.ephemeral.container.volume).
|
||||||
|
For example, /home/esidshi/.airship folder and shown in the snippet below:
|
||||||
|
|
||||||
|
<Snippet>
|
||||||
|
apiVersion: airshipit.org/v1alpha1
|
||||||
|
bootstrapInfo:
|
||||||
|
ephemeral:
|
||||||
|
container:
|
||||||
|
volume: /home/esidshi/.airship:/kube
|
||||||
|
</Snippet>
|
22
bootstrap_capz/azure-config.json
Normal file
22
bootstrap_capz/azure-config.json
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"apiVersion":"v1",
|
||||||
|
"credentials":{
|
||||||
|
"tenant":"<Your Azure Subscription Tenant ID>",
|
||||||
|
"client":"<Your Azure Service Principal ID>",
|
||||||
|
"secret":"<Your Azure Service Principal Secret>"
|
||||||
|
},
|
||||||
|
"kind":"AzureConfig",
|
||||||
|
"metadata":{
|
||||||
|
"name":"capi-azure-zuul"
|
||||||
|
},
|
||||||
|
"region":"centralus",
|
||||||
|
"resourceGroup":"airship2-zuul-rg",
|
||||||
|
"spec":{
|
||||||
|
"cluster":{
|
||||||
|
"k8sVersion":"1.18.6",
|
||||||
|
"kubeconfig":"capz.kubeconfig",
|
||||||
|
"replicas":1,
|
||||||
|
"vmSize":"Standard_B2s"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
bootstrap_capz/azure-config.yaml
Normal file
28
bootstrap_capz/azure-config.yaml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
apiVersion: v1
|
||||||
|
kind: AzureConfig
|
||||||
|
metadata:
|
||||||
|
name: capi-azure-zuul
|
||||||
|
credentials:
|
||||||
|
tenant: <Your Azure Subscription Tenant ID>
|
||||||
|
client: <Your Azure Service Principal ID>
|
||||||
|
secret: <Your Azure Service Principal Secret>
|
||||||
|
spec:
|
||||||
|
resourceGroup: airship2-zuul-rg
|
||||||
|
region: centralus
|
||||||
|
cluster:
|
||||||
|
k8sVersion: 1.18.6
|
||||||
|
vmSize: Standard_B2s
|
||||||
|
replicas: 1
|
||||||
|
kubeconfig: capz.kubeconfig
|
3
bootstrap_capz/bootstrap-env.list
Normal file
3
bootstrap_capz/bootstrap-env.list
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
BOOTSTRAP_COMMAND=help
|
||||||
|
BOOTSTRAP_CONFIG=azure-config.yaml
|
||||||
|
BOOTSTRAP_VOLUME=/tmp:/kube
|
165
bootstrap_capz/config/aks_cluster.go
Normal file
165
bootstrap_capz/config/aks_cluster.go
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
/*
|
||||||
|
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 (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
az = "az"
|
||||||
|
aks = "aks"
|
||||||
|
login = "login"
|
||||||
|
group = "group"
|
||||||
|
create = "create"
|
||||||
|
delete = "delete"
|
||||||
|
getCredentials = "get-credentials"
|
||||||
|
servicePrincipalP = "--service-principal"
|
||||||
|
usernameP = "--username"
|
||||||
|
passwordP = "--password"
|
||||||
|
tenantP = "--tenant"
|
||||||
|
clientSecretP = "--client-secret"
|
||||||
|
resourceGroupP = "--resource-group"
|
||||||
|
locationP = "--location"
|
||||||
|
nameP = "--name"
|
||||||
|
nodeVMSizeP = "--node-vm-size"
|
||||||
|
nodeCountP = "--node-count"
|
||||||
|
k8sVersionP = "--kubernetes-version"
|
||||||
|
generateSSHKeysP = "--generate-ssh-keys"
|
||||||
|
fileP = "--file"
|
||||||
|
yesP = "--yes"
|
||||||
|
|
||||||
|
defaultRegion = "centralus"
|
||||||
|
defaultResourceGroup = "airship2-aks-rg"
|
||||||
|
defaultClusterName = "capi-azure"
|
||||||
|
defaultVMSize = "Standard_B2s"
|
||||||
|
defaultK8SVersion = "1.18.6"
|
||||||
|
defaultKubeconfig = "kubeconfig"
|
||||||
|
)
|
||||||
|
|
||||||
|
// defaulAzureConfig verify if any optional config data is missing.
|
||||||
|
func defaulAzureConfig(azConfig *AzureConfig) error {
|
||||||
|
if azConfig.Spec.Region == "" {
|
||||||
|
azConfig.Spec.Region = defaultRegion
|
||||||
|
}
|
||||||
|
if azConfig.Spec.ResourceGroup == "" {
|
||||||
|
azConfig.Spec.ResourceGroup = defaultResourceGroup
|
||||||
|
}
|
||||||
|
if azConfig.Metadata.Name == "" {
|
||||||
|
azConfig.Metadata.Name = defaultClusterName
|
||||||
|
}
|
||||||
|
if azConfig.Spec.Cluster.VMSize == "" {
|
||||||
|
azConfig.Spec.Cluster.VMSize = defaultVMSize
|
||||||
|
}
|
||||||
|
if azConfig.Spec.Cluster.K8SVersion == "" {
|
||||||
|
azConfig.Spec.Cluster.K8SVersion = defaultK8SVersion
|
||||||
|
}
|
||||||
|
if azConfig.Spec.Cluster.Kubeconfig == "" {
|
||||||
|
azConfig.Spec.Cluster.Kubeconfig = defaultKubeconfig
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepareAKSCluster logs in, create resource group, etc
|
||||||
|
func prepareAKSCluster(azConfig *AzureConfig, isCreate bool) error {
|
||||||
|
// Verify if azure config file provides all information needed for creating a cluster
|
||||||
|
err := defaulAzureConfig(azConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
tenantID := azConfig.Credentials.Tenant
|
||||||
|
clientID := azConfig.Credentials.Client
|
||||||
|
clientSecret := azConfig.Credentials.Secret
|
||||||
|
region := azConfig.Spec.Region
|
||||||
|
resourceGroup := azConfig.Spec.ResourceGroup
|
||||||
|
clusterName := azConfig.Metadata.Name
|
||||||
|
vmSize := azConfig.Spec.Cluster.VMSize
|
||||||
|
nodeCount := strconv.FormatInt(int64(azConfig.Spec.Cluster.Replicas), 10)
|
||||||
|
k8sVersion := azConfig.Spec.Cluster.K8SVersion
|
||||||
|
kubeconfigFile := azConfig.Spec.Cluster.Kubeconfig
|
||||||
|
|
||||||
|
// login to Azure account using Service Principal
|
||||||
|
err = execute(az, login, servicePrincipalP,
|
||||||
|
usernameP, clientID,
|
||||||
|
passwordP, clientSecret,
|
||||||
|
tenantP, tenantID)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to login into Azure using Service Principal\n")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if isCreate {
|
||||||
|
// Create resource group for the AKS cluster
|
||||||
|
err = execute(az, group, create,
|
||||||
|
resourceGroupP, resourceGroup,
|
||||||
|
locationP, region)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to create resource group %s in %s region.\n", resourceGroup, region)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creating Azure AKS cluster
|
||||||
|
err = execute(az, aks, create,
|
||||||
|
resourceGroupP, resourceGroup,
|
||||||
|
nameP, clusterName,
|
||||||
|
servicePrincipalP, clientID,
|
||||||
|
clientSecretP, clientSecret,
|
||||||
|
locationP, region,
|
||||||
|
nodeVMSizeP, vmSize,
|
||||||
|
nodeCountP, nodeCount,
|
||||||
|
k8sVersionP, k8sVersion,
|
||||||
|
generateSSHKeysP)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to create AKS cluster %s in %s region.\n", clusterName, region)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get Kubeconfig filename
|
||||||
|
volMount := os.Getenv("BOOTSTRAP_VOLUME")
|
||||||
|
_, dstMount := GetVolumeMountPoints(volMount)
|
||||||
|
|
||||||
|
kubeconfig := dstMount + "/" + kubeconfigFile
|
||||||
|
|
||||||
|
// Delete existing Kubeconfig file, if any
|
||||||
|
err = os.Remove(kubeconfig)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to remove existing kubeconfig file %s.\n", kubeconfig)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieving the Kubeconfig file for the cluster
|
||||||
|
err = execute(az, aks, getCredentials,
|
||||||
|
resourceGroupP, resourceGroup,
|
||||||
|
nameP, clusterName,
|
||||||
|
fileP, kubeconfig)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to retrieve kubeconfig file for AKS cluster %s in %s region.\n", clusterName, region)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Delete Azure AKS cluster
|
||||||
|
err = execute(az, aks, delete,
|
||||||
|
resourceGroupP, resourceGroup,
|
||||||
|
nameP, clusterName, yesP)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to delete AKS cluster %s in %s region.\n", clusterName, region)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
101
bootstrap_capz/config/azure_cluster.go
Normal file
101
bootstrap_capz/config/azure_cluster.go
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
/*
|
||||||
|
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 (
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
bootstrapHelpFile = "help.txt"
|
||||||
|
|
||||||
|
// BootstrapCommand environment variable
|
||||||
|
bootstrapHome = "HOME"
|
||||||
|
bootstrapCommand = "BOOTSTRAP_COMMAND"
|
||||||
|
bootstrapConfig = "BOOTSTRAP_CONFIG"
|
||||||
|
bootstrapVolume = "BOOTSTRAP_VOLUME"
|
||||||
|
bootstrapVolumeSep = ":"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetVolumeMountPoints extracts the source and destination of a volume mount
|
||||||
|
func GetVolumeMountPoints(volumeMount string) (string, string) {
|
||||||
|
sepPos := strings.Index(volumeMount, bootstrapVolumeSep)
|
||||||
|
|
||||||
|
srcMountPoint := volumeMount[:sepPos]
|
||||||
|
dstMountPoint := volumeMount[sepPos+1:]
|
||||||
|
|
||||||
|
return srcMountPoint, dstMountPoint
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute bash command
|
||||||
|
func execute(command string, arg ...string) error {
|
||||||
|
cmd := exec.Command(command, arg...)
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
|
if err := cmd.Start(); err != nil {
|
||||||
|
log.Printf("Error executing script %s\n", command)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cmd.Wait(); err != nil {
|
||||||
|
log.Printf("Error waiting for command execution: %s", err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateAKSCluster creates the AKS cluster
|
||||||
|
func CreateAKSCluster(config *AzureConfig) error {
|
||||||
|
return prepareAKSCluster(config, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteAKSCluster deletes the AKS cluster
|
||||||
|
func DeleteAKSCluster(config *AzureConfig) error {
|
||||||
|
return prepareAKSCluster(config, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HelpAKSCluster returns the help.txt for the AKS cluster
|
||||||
|
func HelpAKSCluster() error {
|
||||||
|
homeDir := os.Getenv(bootstrapHome)
|
||||||
|
src := homeDir + "/" + bootstrapHelpFile
|
||||||
|
in, err := os.Open(src)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Could not open %s file\n", src)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer in.Close()
|
||||||
|
|
||||||
|
_, dstMountPoint := GetVolumeMountPoints(os.Getenv(bootstrapVolume))
|
||||||
|
dst := dstMountPoint + "/" + bootstrapHelpFile
|
||||||
|
out, err := os.Create(dst)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Could not create %s file\n", dst)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer out.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(out, in)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to copy %s file to %s\n", src, dst)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return out.Close()
|
||||||
|
}
|
137
bootstrap_capz/config/azure_config.go
Normal file
137
bootstrap_capz/config/azure_config.go
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
/*
|
||||||
|
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 (
|
||||||
|
"errors"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"gopkg.in/go-playground/validator.v9"
|
||||||
|
"sigs.k8s.io/yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AzureConfig holds configurations for bootstrap steps
|
||||||
|
type AzureConfig struct {
|
||||||
|
// +optional
|
||||||
|
Kind string `json:"kind" validate:"required"`
|
||||||
|
|
||||||
|
// +optional
|
||||||
|
APIVersion string `json:"apiVersion" validate:"required"`
|
||||||
|
|
||||||
|
// Configuration parameters for metadata
|
||||||
|
Metadata *Metadata `json:"metadata"`
|
||||||
|
|
||||||
|
// Configuration parameters for metadata
|
||||||
|
Credentials *Credentials `json:"credentials" validate:"required"`
|
||||||
|
|
||||||
|
// Configuration parameters for spec
|
||||||
|
Spec *Spec `json:"spec"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Metadata structure provides the cluster name to assign and labels to the k8s cluster
|
||||||
|
type Metadata struct {
|
||||||
|
Name string `json:"name" validate:"required"`
|
||||||
|
Labels []string `json:"labels,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Credentials structu provides the credentials to authenticate with Azure Cloud
|
||||||
|
type Credentials struct {
|
||||||
|
Tenant string `json:"tenant" validate:"required"`
|
||||||
|
Client string `json:"client" validate:"required"`
|
||||||
|
Secret string `json:"secret" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spec structure contains the info for the ck8s luster to deploy
|
||||||
|
type Spec struct {
|
||||||
|
Region string `json:"region,omitempty"`
|
||||||
|
ResourceGroup string `json:"resourceGroup,omitempty"`
|
||||||
|
Cluster Cluster `json:"cluster"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cluster struct provides data for the k8s cluster to deploy
|
||||||
|
type Cluster struct {
|
||||||
|
// Kubernetes version to deploy
|
||||||
|
K8SVersion string `json:"k8sVersion,omitempty"`
|
||||||
|
|
||||||
|
// Azure VM size to use for the cluster
|
||||||
|
VMSize string `json:"vmSize,omitempty"`
|
||||||
|
|
||||||
|
// Number of nodes to deploy for the cluster
|
||||||
|
Replicas uint8 `json:"replicas,omitempty" validate:"gte=1,lte=100"`
|
||||||
|
|
||||||
|
// Kubeconfig filename to save
|
||||||
|
Kubeconfig string `json:"kubeconfig,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadYAMLFile reads YAML-formatted configuration file and
|
||||||
|
// de-serializes it to a given object
|
||||||
|
func ReadYAMLFile(filePath string, cfg *AzureConfig) error {
|
||||||
|
data, err := ioutil.ReadFile(filePath)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("yamlFile.Get err #%v ", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return yaml.Unmarshal(data, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadYAMLtoJSON reads YAML-formatted configuration file and
|
||||||
|
// de-serializes it to a given object
|
||||||
|
func ReadYAMLtoJSON(filePath string) (string, error) {
|
||||||
|
data, err := ioutil.ReadFile(filePath)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to read Azure Ephemeral configuration file: err #%v ", err)
|
||||||
|
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
jsonByte, err := yaml.YAMLToJSON(data)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("YAMLtoJSON err #%v ", err)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
jsonStr := string(jsonByte)
|
||||||
|
return jsonStr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateConfigFile validates Azure configuration file for the Ephemeral Cluster
|
||||||
|
func ValidateConfigFile(config *AzureConfig) error {
|
||||||
|
var validate *validator.Validate
|
||||||
|
|
||||||
|
validate = validator.New()
|
||||||
|
err := validate.Struct(config)
|
||||||
|
if err != nil {
|
||||||
|
// this check is only needed when your code could produce
|
||||||
|
// an invalid value for validation such as interface with nil value.
|
||||||
|
var invalidError *validator.InvalidValidationError
|
||||||
|
if errors.As(err, &invalidError) {
|
||||||
|
log.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("Ephemeral cluster configuration file validation failed")
|
||||||
|
for _, err := range err.(validator.ValidationErrors) {
|
||||||
|
log.Printf(" Namespace = %s\n", err.Namespace())
|
||||||
|
log.Printf(" Tag = %s\n", err.Tag())
|
||||||
|
log.Printf(" Type = %s\n", err.Type())
|
||||||
|
log.Printf(" Value = %s\n", err.Value())
|
||||||
|
log.Printf(" Param = %s\n", err.Param())
|
||||||
|
log.Println()
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
78
bootstrap_capz/main.go
Normal file
78
bootstrap_capz/main.go
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
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 (
|
||||||
|
"flag"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"opendev.org/airship/images/bootstrap_capz/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
createCmd = "create"
|
||||||
|
deleteCmd = "delete"
|
||||||
|
helpCmd = "help"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var configPath string
|
||||||
|
|
||||||
|
flag.StringVar(&configPath, "c", "", "Path for the Azure bootstrap configuration (yaml) file")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if configPath == "" {
|
||||||
|
volMount := os.Getenv("BOOTSTRAP_VOLUME")
|
||||||
|
_, dstMount := config.GetVolumeMountPoints(volMount)
|
||||||
|
azureConfigPath := dstMount + "/" + os.Getenv("BOOTSTRAP_CONFIG")
|
||||||
|
configPath = azureConfigPath
|
||||||
|
}
|
||||||
|
|
||||||
|
configYAML := &config.AzureConfig{}
|
||||||
|
err := config.ReadYAMLFile(configPath, configYAML)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Failed to load Azure Bootstrap config file")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = config.ValidateConfigFile(configYAML)
|
||||||
|
if err != nil {
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
command := os.Getenv("BOOTSTRAP_COMMAND")
|
||||||
|
switch {
|
||||||
|
case command == createCmd:
|
||||||
|
err = config.CreateAKSCluster(configYAML)
|
||||||
|
if err != nil {
|
||||||
|
os.Exit(5)
|
||||||
|
}
|
||||||
|
case command == deleteCmd:
|
||||||
|
err = config.DeleteAKSCluster(configYAML)
|
||||||
|
if err != nil {
|
||||||
|
os.Exit(6)
|
||||||
|
}
|
||||||
|
case command == helpCmd:
|
||||||
|
err = config.HelpAKSCluster()
|
||||||
|
if err != nil {
|
||||||
|
os.Exit(7)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
log.Printf("The --command parameter value shall be 'create', 'delete' or 'help'")
|
||||||
|
os.Exit(8)
|
||||||
|
}
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user