add installKubectlAndTools

This commit is contained in:
az 2021-10-09 11:54:10 +02:00
parent 73a40498a0
commit 1493977672
6 changed files with 172 additions and 89 deletions

View file

@ -40,22 +40,25 @@ fun getCallingMethodName(): String? {
} }
fun String.escapeNewline(): String = this.replace("\r", "\\r").replace("\n", "\\n") fun String.escapeNewline(): String = replace("\r", "\\r").replace("\n", "\\n")
fun String.escapeControlChars(): String = this.replace("\r", "\\r").replace("\n", "\\n").replace("\t", "\\t").replace("[\\p{Cntrl}]".toRegex(), "\\?") fun String.escapeControlChars(): String = replace("\r", "\\r").replace("\n", "\\n").replace("\t", "\\t").replace("[\\p{Cntrl}]".toRegex(), "\\?")
fun String.escapeBackslash(): String = this.replace("\\", "\\\\") fun String.escapeBackslash(): String = replace("\\", "\\\\")
fun String.escapeDoubleQuote(): String = this.replace("\"", "\\\"") fun String.escapeDoubleQuote(): String = replace("\"", "\\\"")
fun String.escapeSingleQuote(): String = this.replace("'", "\'") fun String.escapeSingleQuote(): String = replace("'", "\'")
fun String.escapeSingleQuoteForShell(): String = this.replace("'", "'\"'\"'") fun String.escapeSingleQuoteForShell(): String = replace("'", "'\"'\"'")
fun String.escapeProcentForPrintf(): String = this.replace("%", "%%") fun String.escapeProcentForPrintf(): String = replace("%", "%%")
fun String.endingWithFileSeparator(): String = if (length > 0 && (last() != fileSeparatorChar())) this + fileSeparator() else this
// see https://www.shellscript.sh/escape.html // see https://www.shellscript.sh/escape.html
fun String.escapeAndEncloseByDoubleQuoteForShell(): String { fun String.escapeAndEncloseByDoubleQuoteForShell(): String {
return "\"" + this.escapeBackslash().replace("`", "\\`").escapeDoubleQuote().replace("$", "\\$") + "\"" return "\"" + this.escapeBackslash().replace("`", "\\`").escapeDoubleQuote().replace("$", "\\$") + "\""
} }
fun hostUserHome(): String = System.getProperty("user.home") + fileSeparator()
fun newline(): String = System.getProperty("line.separator")
fun fileSeparator(): String = File.separator fun fileSeparator(): String = File.separator
fun fileSeparatorChar(): Char = File.separatorChar
fun newline(): String = System.getProperty("line.separator")
fun hostUserHome(): String = System.getProperty("user.home") + fileSeparator()
fun getResourceAsText(path: String): String { fun getResourceAsText(path: String): String {
val resource = Thread.currentThread().contextClassLoader.getResource(path) val resource = Thread.currentThread().contextClassLoader.getResource(path)

View file

@ -10,6 +10,22 @@ fun Prov.fileExists(file: String, sudo: Boolean = false): Boolean {
} }
fun Prov.createFileFromResource(
fullyQualifiedFilename: String,
resourceFilename: String,
resourcePath: String = "",
posixFilePermission: String? = null,
sudo: Boolean = false
): ProvResult {
return createFile(
fullyQualifiedFilename,
getResourceAsText(resourcePath.endingWithFileSeparator() + resourceFilename),
posixFilePermission,
sudo
)
}
fun Prov.createFile( fun Prov.createFile(
fullyQualifiedFilename: String, fullyQualifiedFilename: String,
text: String?, text: String?,
@ -69,6 +85,14 @@ fun Prov.fileContent(file: String, sudo: Boolean = false): String? {
} }
fun Prov.addTextToFile(
text: String,
file: String,
doNotAddIfExisting: Boolean = true,
sudo: Boolean = false
): ProvResult = addTextToFile(text, File(file), doNotAddIfExisting, sudo)
fun Prov.addTextToFile( fun Prov.addTextToFile(
text: String, text: String,
file: File, file: File,

View file

@ -2,19 +2,16 @@ package org.domaindrivenarchitecture.provs.workplace.infrastructure
import org.domaindrivenarchitecture.provs.core.Prov import org.domaindrivenarchitecture.provs.core.Prov
import org.domaindrivenarchitecture.provs.core.ProvResult import org.domaindrivenarchitecture.provs.core.ProvResult
import org.domaindrivenarchitecture.provs.ubuntu.filesystem.base.createDirs import org.domaindrivenarchitecture.provs.ubuntu.filesystem.base.*
import org.domaindrivenarchitecture.provs.ubuntu.filesystem.base.createFile
import org.domaindrivenarchitecture.provs.ubuntu.filesystem.base.dirExists
import org.domaindrivenarchitecture.provs.ubuntu.filesystem.base.fileExists
import org.domaindrivenarchitecture.provs.ubuntu.install.base.aptInstall import org.domaindrivenarchitecture.provs.ubuntu.install.base.aptInstall
import org.domaindrivenarchitecture.provs.ubuntu.web.base.downloadFromURL import org.domaindrivenarchitecture.provs.ubuntu.web.base.downloadFromURL
fun Prov.installDevOps() = def { fun Prov.installDevOps() = def {
installTerraform() installTerraform()
//installAwsCredentials("", "") // TODO: get credentials from gopass installKubectlAndTools()
installKubectl()
installYq() installYq()
installAwsCredentials()
} }
@ -22,10 +19,10 @@ fun Prov.installYq(
version: String = "4.13.2", version: String = "4.13.2",
sha256sum: String = "d7c89543d1437bf80fee6237eadc608d1b121c21a7cbbe79057d5086d74f8d79" sha256sum: String = "d7c89543d1437bf80fee6237eadc608d1b121c21a7cbbe79057d5086d74f8d79"
): ProvResult = def { ): ProvResult = def {
var path = "/usr/bin/" val path = "/usr/bin/"
var filename = "yq" val filename = "yq"
if(!fileExists(path + filename)) { if (!fileExists(path + filename)) {
val result = downloadFromURL( downloadFromURL(
"https://github.com/mikefarah/yq/releases/download/v$version/yq_linux_amd64", "https://github.com/mikefarah/yq/releases/download/v$version/yq_linux_amd64",
filename, filename,
path, path,
@ -38,97 +35,82 @@ fun Prov.installYq(
} }
} }
fun Prov.installKubectl(): ProvResult = def { fun Prov.installKubectlAndTools(): ProvResult = def {
var kubeConfigFile = "~/.bashrc.d/kubectl.sh" val resourcePath = "workplace/infrastructure/"
if(!fileExists(kubeConfigFile)) {
aptInstall("kubectl")
cmd("kubectl completion bash >> /etc/bash_completion.d/kubernetes", sudo = true)
// TODO: externalize to file - trippeld escaping is realy ugly & does not work
/*var kubeConfig = """
# Set the default kube context if present
DEFAULT_KUBE_CONTEXTS="$HOME/.kube/config"
if test -f "${DEFAULT_KUBE_CONTEXTS}"
then
export KUBECONFIG="$DEFAULT_KUBE_CONTEXTS"
fi
# Additional contexts should be in ~/.kube/custom-contexts/ task("installKubectl") {
CUSTOM_KUBE_CONTEXTS="$HOME/.kube/custom-contexts" val kubeConfigFile = "~/.bashrc.d/kubectl.sh"
mkdir -p "${CUSTOM_KUBE_CONTEXTS}" if (!fileExists(kubeConfigFile)) {
// prerequisites -- see https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/
cmd("sudo apt-get update")
aptInstall("apt-transport-https ca-certificates curl")
cmd("sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg")
cmd("echo \"deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main\" | sudo tee /etc/apt/sources.list.d/kubernetes.list")
OIFS="$IFS" // kubectl and bash completion
IFS=$'\n' cmd("sudo apt-get update")
for contextFile in `find "${CUSTOM_KUBE_CONTEXTS}" -type f -name "*.yml"` aptInstall("kubectl")
do addTextToFile("\nkubectl completion bash\n", "/etc/bash_completion.d/kubernetes", sudo = true)
export KUBECONFIG="$contextFile:$KUBECONFIG" createFileFromResource(kubeConfigFile, "kubectl.sh", resourcePath)
done } else {
IFS="$OIFS" ProvResult(true, out = "Kubectl already installed")
""".trimIndent()
*/
}
val tunnelAliasFile = "~/.bashrc.d/ssh_alias.sh"
if(!fileExists(tunnelAliasFile)) {
var tunnelAlias = """
alias sshu='ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
alias ssht='ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -L 8002:localhost:8002 -L 6443:192.168.5.1:6443'
""".trimIndent()
createFile(tunnelAliasFile, tunnelAlias, "640")
}
val k8sContextFile = "/usr/local/bin/k8s-create-context.sh"
if(!fileExists(k8sContextFile)) {
// TODO: externalize to file - trippeld escaping is realy ugly & does not work
var k8sContext = """
function main() {
local cluster_name="${1}"; shift
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@\$\{cluster_name}.meissa-gmbh.de \
"cat /etc/kubernetes/admin.conf" | \
yq e ".clusters[0].name=\"\$\{cluster_name}\" \
| .clusters[0].cluster.server=\"https://kubernetes:6443\" \
| .contexts[0].context.cluster=\"\$\{cluster_name}\" \
| .contexts[0].context.user=\"\$\{cluster_name}\" \
| .contexts[0].name=\"\$\{cluster_name}\" \
| del(.current-context) \
| del(.preferences) \
| .users[0].name=\"\$\{cluster_name}\"" - \
> ~/.kube/custom-contexts/\$\{cluster_name}.yml
} }
}
main $1 task("install tunnel alias") {
val tunnelAliasFile = "~/.bashrc.d/ssh_alias.sh"
if (!fileExists(tunnelAliasFile)) {
val tunnelAlias = """
alias sshu='ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no'
alias ssht='ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -L 8002:localhost:8002 -L 6443:192.168.5.1:6443'
""".trimIndent()
createFile(tunnelAliasFile, tunnelAlias, "640")
} else {
ProvResult(true, out = "tunnel alias already installed")
}
}
""".trimIndent() task("install k8sCreateContext") {
createFile(k8sContextFile, k8sContext, "555", sudo = true) val k8sContextFile = "/usr/local/bin/k8s-create-context.sh"
} else { if (!fileExists(k8sContextFile)) {
ProvResult(true) createFileFromResource(
k8sContextFile,
"k8s-create-context.sh",
resourcePath,
"555",
sudo = true
)
} else {
ProvResult(true)
}
} }
} }
fun Prov.installTerraform(): ProvResult = def { fun Prov.installTerraform(): ProvResult = def {
val dir = "/usr/lib/tfenv/" val dir = "/usr/lib/tfenv/"
if(!dirExists(dir)) { if (!dirExists(dir)) {
createDirs(dir, sudo = true) createDirs(dir, sudo = true)
cmd("git clone https://github.com/tfutils/tfenv.git " + dir, sudo = true) cmd("git clone https://github.com/tfutils/tfenv.git " + dir, sudo = true)
cmd("rm " + dir + ".git/ -rf", sudo = true) cmd("rm " + dir + ".git/ -rf", sudo = true)
cmd("ln -s " + dir + "bin/* /usr/local/bin", sudo = true) cmd("ln -s " + dir + "bin/* /usr/local/bin", sudo = true)
} }
cmd ("tfenv install", sudo = true) cmd("tfenv install", sudo = true)
cmd ("tfenv install latest:^0.13", sudo = true) cmd("tfenv install latest:^1.0.8", sudo = true)
cmd ("tfenv use latest:^0.13", sudo = true) cmd("tfenv use latest:^1.0.8", sudo = true)
} }
fun Prov.installAwsCredentials(id:String, key:String): ProvResult = def {
// -------------------------------------------- AWS credentials file -----------------------------------------------
fun Prov.installAwsCredentials(id: String = "REPLACE_WITH_YOUR_ID", key: String = "REPLACE_WITH_YOUR_KEY"): ProvResult = def {
val dir = "~/.aws" val dir = "~/.aws"
if(!dirExists(dir)) { if (!dirExists(dir)) {
createDirs(dir) createDirs(dir)
createFile("~/.aws/config", awsConfig()) createFile("~/.aws/config", awsConfig())
createFile("~/.aws/credentials", awsCredentials(id, key)) createFile("~/.aws/credentials", awsCredentials(id, key))
} else { } else {
ProvResult(true, "aws credential file already installed") ProvResult(true, "aws credential folder already installed")
} }
} }
@ -140,7 +122,7 @@ fun awsConfig(): String {
""".trimIndent() """.trimIndent()
} }
fun awsCredentials(id:String, key:String): String { fun awsCredentials(id: String, key: String): String {
return """ return """
[default] [default]
aws_access_key_id = $id aws_access_key_id = $id

View file

@ -0,0 +1,17 @@
function main() {
local cluster_name="${1}"; shift
ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no root@${cluster_name}.meissa-gmbh.de \
"cat /etc/kubernetes/admin.conf" | \
yq e ".clusters[0].name=\"${cluster_name}\" \
| .clusters[0].cluster.server=\"https://kubernetes:6443\" \
| .contexts[0].context.cluster=\"${cluster_name}\" \
| .contexts[0].context.user=\"${cluster_name}\" \
| .contexts[0].name=\"${cluster_name}\" \
| del(.current-context) \
| del(.preferences) \
| .users[0].name=\"${cluster_name}\"" - \
> ~/.kube/custom-contexts/${cluster_name}.yml
}
main $1

View file

@ -0,0 +1,18 @@
# Set the default kube context if present
DEFAULT_KUBE_CONTEXTS="$HOME/.kube/config"
if test -f "${DEFAULT_KUBE_CONTEXTS}"
then
export KUBECONFIG="$DEFAULT_KUBE_CONTEXTS"
fi
# Additional contexts should be in ~/.kube/custom-contexts/
CUSTOM_KUBE_CONTEXTS="$HOME/.kube/custom-contexts"
mkdir -p "${CUSTOM_KUBE_CONTEXTS}"
OIFS="$IFS"
IFS=$'\n'
for contextFile in $(find "${CUSTOM_KUBE_CONTEXTS}" -type f -name "*.yml")
do
export KUBECONFIG="$contextFile:$KUBECONFIG"
done
IFS="$OIFS"

View file

@ -0,0 +1,39 @@
package org.domaindrivenarchitecture.provs.workplace.infrastructure
import org.domaindrivenarchitecture.provs.core.getResourceAsText
import org.domaindrivenarchitecture.provs.test.defaultTestContainer
import org.domaindrivenarchitecture.provs.ubuntu.filesystem.base.createDir
import org.domaindrivenarchitecture.provs.ubuntu.filesystem.base.createDirs
import org.domaindrivenarchitecture.provs.ubuntu.filesystem.base.fileContainsText
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test
internal class DevOpsKtTest {
@Test
fun installKubectlAndTools() {
// given
defaultTestContainer().def {
createDirs("/etc/bash_completion.d", sudo = true)
createDir(".bashrc.d")
}
//when
val res = defaultTestContainer().installKubectlAndTools()
// then
assertTrue(res.success)
assertTrue(
defaultTestContainer().fileContainsText(
"~/.bashrc.d/kubectl.sh",
getResourceAsText("workplace/infrastructure/kubectl.sh")
)
)
assertTrue(
defaultTestContainer().fileContainsText(
"/etc/bash_completion.d/kubernetes",
"\nkubectl completion bash\n"
)
)
}
}