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/
|
||||
bootstrap_capg/.vscode/launch.json
|
||||
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