add installKubectlAndTools
This commit is contained in:
parent
73a40498a0
commit
1493977672
6 changed files with 172 additions and 89 deletions
|
@ -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)
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
18
src/main/resources/workplace/infrastructure/kubectl.sh
Normal file
18
src/main/resources/workplace/infrastructure/kubectl.sh
Normal 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"
|
|
@ -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"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue