From 0d55be71eaf4f696a65b30e8196c89f2bc4a6bce Mon Sep 17 00:00:00 2001 From: fj0r <82698591+fj0r@users.noreply.github.com> Date: Thu, 4 Jan 2024 22:14:53 +0800 Subject: [PATCH] kubernetes to docker-compose (#726) - kubernetes to docker-compose - rearrange helm related commands Co-authored-by: nash --- modules/kubernetes/kubernetes.nu | 397 +++++++++++++++++++++---------- 1 file changed, 277 insertions(+), 120 deletions(-) diff --git a/modules/kubernetes/kubernetes.nu b/modules/kubernetes/kubernetes.nu index 4bdecba..ab74420 100644 --- a/modules/kubernetes/kubernetes.nu +++ b/modules/kubernetes/kubernetes.nu @@ -79,126 +79,6 @@ export def kk [p: path] { kubectl kustomize $p } -# helm list and get -export def kgh [ - name?: string@"nu-complete helm list" - --namespace (-n): string@"nu-complete kube ns" - --manifest (-m) - --values(-v) - --all (-a) -] { - if ($name | is-empty) { - let ns = if $all { [--all] } else { $namespace | with-flag -n } - helm list $ns --output json - | from json - | update updated {|x| - $x.updated - | str substring ..-4 - | into datetime -f '%Y-%m-%d %H:%M:%S.%f %z' - } - } else { - if $manifest { - helm get manifest $name ($namespace | with-flag -n) - } else if $values { - helm get values $name ($namespace | with-flag -n) - } else { - helm get notes $name ($namespace | with-flag -n) - } - } -} - -def "nu-complete helm list" [context: string, offset: int] { - let ctx = $context | argx parse - kgh -n $ctx.namespace? | each {|x| {value: $x.name description: $x.updated} } -} - -def "nu-complete helm charts" [context: string, offset: int] { - let ctx = $context | argx parse - let path = $ctx | get _pos.chart - let path = if ($path | is-empty) { '.' } else { $path } - let paths = do -i { ls $"($path)*" | each {|x| if $x.type == dir { $"($x.name)/"} else { $x.name }} } - helm repo list | from ssv -a | rename value description - | append $paths -} - -def record-to-set-json [value] { - $value | transpose k v - | each {|x| $"($x.k)=($x.v | to json -r)"} - | str join ',' -} - -# helm install or upgrade via values file -export def kah [ - name: string@"nu-complete helm list" - chart: string@"nu-complete helm charts" - valuefile: path - --values (-v): any - --namespace (-n): string@"nu-complete kube ns" -] { - let update = $name in ( - helm list ($namespace | with-flag -n) --output json - | from json | get name - ) - let act = if $update { [upgrade] } else { [install] } - let values = if ($values | is-empty) { [] } else { [--set-json (record-to-set-json $values)] } - helm $act $name $chart -f $valuefile $values ($namespace | with-flag -n) -} - -# helm diff -export def kdh [ - name: string@"nu-complete helm list" - chart: string@"nu-complete helm charts" - valuefile: path - --values (-v): any - --namespace (-n): string@"nu-complete kube ns" - --ignore-image (-i) - --has-plugin (-h) -] { - if $has_plugin { - helm diff $name $chart -f $valuefile ($namespace | with-flag -n) - } else { - let update = $name in ( - helm list ($namespace | with-flag -n) --output json - | from json | get name - ) - if not $update { - echo "new installation" - return - } - - let values = if ($values | is-empty) { [] } else { [--set-json (record-to-set-json $values)] } - let target = $'/tmp/($chart | path basename).($name).out.yaml' - helm template --debug $name $chart -f $valuefile $values ($namespace | with-flag -n) | save -f $target - if $ignore_image { - do -i { yq -i ea 'del(.spec.template.spec.containers.[].image)' $target } - } - kubectl diff -f $target - } -} - -# helm delete -export def kdelh [ - name: string@"nu-complete helm list" - --namespace (-n): string@"nu-complete kube ns" -] { - helm uninstall $name ($namespace | with-flag -n) -} - -# helm template -export def kh [ - chart: string@"nu-complete helm charts" - valuefile: path - --values (-v): any - --namespace (-n): string@"nu-complete kube ns"='test' - --app (-a): string='test' -] { - let values = if ($values | is-empty) { [] } else { [--set-json (record-to-set-json $values)] } - let target = $valuefile | split row '.' | range ..-2 | append [out yaml] | str join '.' - if (not ($target | path exists)) and (([yes no] | input list $'create ($target)?') in [no]) { return } - helm template --debug $app $chart -f $valuefile $values ($namespace | with-flag -n) - | save -f $target -} - ### ctx export def "kube-config" [] { let file = if ($env.KUBECONFIG? | is-empty) { $"($env.HOME)/.kube/config" } else { $env.KUBECONFIG } @@ -884,3 +764,280 @@ export def kgcert [] { kubectl get order.acme -o wide | from ssv | rename order.acme kubectl get challenges.acme -o wide | from ssv | rename challenges.acme } + +# kubernetes to docker-compose +export def kcmp [--without-service(-s)] { + let a = $in + let dpl = $a | where kind == 'Deployment' + mut dpl_svc = {} + mut svc = {} + if not $without_service { + let svcs = $a | where kind == 'Service' + for s in $svcs { + for d in $dpl { + let dl = $d.metadata.labels? + let ss = $s.spec.selector? + mut p = true + for x in ($ss | transpose k v) { + if (not ($x.k in $dl)) or (($dl | get $x.k) != $x.v) { + $p = false + break + } + } + if $p { + let dn = $d.metadata.name + let dv = if $dn in $dpl_svc { + $dpl_svc | get $dn + } else { [] } + $dpl_svc = ($dpl_svc | upsert $dn ($dv | append $s.metadata.name)) + } + } + } + $svc = ($svcs + | reduce -f {} {|i, a| + $a | upsert $i.metadata.name $i.spec.ports + }) + } + let dpl_svc = $dpl_svc + let svc = $svc + + let cm = $a | where kind == 'ConfigMap' + | reduce -f {} {|i,a| + let p = ['.kcmp', 'ConfigMap', $i.metadata.name] | path join + mkdir $p + for f in ($i.data | transpose k v) { + $f.v | save -f ([$p $f.k] | path join) + } + $a | insert $i.metadata.name $p + } + let sec = $a | where kind == 'Secret' + | reduce -f {} {|i,a| + let p = ['.kcmp', 'Secret', $i.metadata.name] | path join + mkdir $p + for f in ($i.data | transpose k v) { + $f.v | decode base64 | save -f ([$p $f.k] | path join) + } + $a | insert $i.metadata.name $p + } + let pv = $a | where kind == 'PersistentVolume' + | reduce -f {} {|i,a| $a | insert $i.metadata.name $i.spec.local?.path? } + let pvc = $a | where kind == 'PersistentVolumeClaim' + | reduce -f {} {|i,a| $a | insert $i.metadata.name ($pv | get $i.spec.volumeName) } + + let services = $dpl + | each {|x| + let d = $x.metadata.name + let s = if not $without_service { + $dpl_svc + | get $d + | each {|z| $svc | get $z} + | flatten + | each {|z| [$z.port $z.targetPort] } + } + + let v = $x.spec.template.spec.volumes? + | reduce -f {} {|i,a| + $a | insert $i.name $i + } + + $x.spec.template.spec.containers? + | default [] + | each {|y| + let ca = $y.securityContext?.capabilities?.add? + let cd = $y.securityContext?.capabilities?.drop? + let v = $y.volumeMounts? + | default [] + | each {|z| + let sp = $z.subPath? | default '' + let s = $v | get $z.name + let s = if not ($s.hostPath? | is-empty) { + $s.hostPath.path + } else if not ($s.path? | is-empty) { + $s.path + } else if not ($s.persistentVolumeClaim?.claimName? | is-empty) { + $pvc | get $s.persistentVolumeClaim.claimName + } else if not ($s.configMap?.name? | is-empty) { + ['.' + ($cm | get $s.configMap.name) + $sp + ] | path join + } else if not ($s.secret?.secretName? | is-empty) { + ['.' + ($sec | get $s.secret.secretName) + $sp + ] | path join + } else { + $s + } + let m = [$z.mountPath $sp] | path join + $"($s):($m)" + } + let s = if $without_service { + $y.ports? | default [] | each {|x| $"($x.containerPort):($x.containerPort)"} + } else { + $s | each {|x| + if ($x.1 | find -r '[^0-9]' | is-empty) { + $"($x.0):($x.1)" + } else { + mut p = '' + for i in ($y.ports? | default []) { + if $i.name == $x.1 { + $p = $i.containerPort + break + } + } + $"($x.0):($p)" + } + } + } + + { + name: $"($d)_($y.name)" + image: $y.image + environment: ($y.env? | reduce -f {} {|i,a| $a | insert $i.name $i.value?}) + ports: $s + entrypoint: $y.command? + command: $y.args? + cap_add: $ca + cap_drop: $cd + volumes: $v + } + | transpose k v + | reduce -f {} {|i,a| + if ($i.v | is-empty) { + $a + } else { + $a | insert $i.k $i.v + } + } + } + } + | flatten + | reduce -f {} {|i,a| + $a | insert $i.name ($i | reject name) + } + + { version: '3.8', services: $services} + | to yaml +} + +# helm list and get +export def kgh [ + name?: string@"nu-complete helm list" + --namespace (-n): string@"nu-complete kube ns" + --manifest (-m) + --values(-v) + --all (-a) +] { + if ($name | is-empty) { + let ns = if $all { [--all] } else { $namespace | with-flag -n } + helm list $ns --output json + | from json + | update updated {|x| + $x.updated + | str substring ..-4 + | into datetime -f '%Y-%m-%d %H:%M:%S.%f %z' + } + } else { + if $manifest { + helm get manifest $name ($namespace | with-flag -n) + } else if $values { + helm get values $name ($namespace | with-flag -n) + } else { + helm get notes $name ($namespace | with-flag -n) + } + } +} + +def "nu-complete helm list" [context: string, offset: int] { + let ctx = $context | argx parse + kgh -n $ctx.namespace? | each {|x| {value: $x.name description: $x.updated} } +} + +def "nu-complete helm charts" [context: string, offset: int] { + let ctx = $context | argx parse + let path = $ctx | get _pos.chart + let path = if ($path | is-empty) { '.' } else { $path } + let paths = do -i { ls $"($path)*" | each {|x| if $x.type == dir { $"($x.name)/"} else { $x.name }} } + helm repo list | from ssv -a | rename value description + | append $paths +} + +def record-to-set-json [value] { + $value | transpose k v + | each {|x| $"($x.k)=($x.v | to json -r)"} + | str join ',' +} + +# helm install or upgrade via values file +export def kah [ + name: string@"nu-complete helm list" + chart: string@"nu-complete helm charts" + valuefile: path + --values (-v): any + --namespace (-n): string@"nu-complete kube ns" +] { + let update = $name in ( + helm list ($namespace | with-flag -n) --output json + | from json | get name + ) + let act = if $update { [upgrade] } else { [install] } + let values = if ($values | is-empty) { [] } else { [--set-json (record-to-set-json $values)] } + helm $act $name $chart -f $valuefile $values ($namespace | with-flag -n) +} + +# helm diff +export def kdh [ + name: string@"nu-complete helm list" + chart: string@"nu-complete helm charts" + valuefile: path + --values (-v): any + --namespace (-n): string@"nu-complete kube ns" + --ignore-image (-i) + --has-plugin (-h) +] { + if $has_plugin { + helm diff $name $chart -f $valuefile ($namespace | with-flag -n) + } else { + let update = $name in ( + helm list ($namespace | with-flag -n) --output json + | from json | get name + ) + if not $update { + echo "new installation" + return + } + + let values = if ($values | is-empty) { [] } else { [--set-json (record-to-set-json $values)] } + let target = $'/tmp/($chart | path basename).($name).out.yaml' + helm template --debug $name $chart -f $valuefile $values ($namespace | with-flag -n) | save -f $target + if $ignore_image { + do -i { yq -i ea 'del(.spec.template.spec.containers.[].image)' $target } + } + kubectl diff -f $target + } +} + +# helm delete +export def kdelh [ + name: string@"nu-complete helm list" + --namespace (-n): string@"nu-complete kube ns" +] { + helm uninstall $name ($namespace | with-flag -n) +} + +# helm template +export def kh [ + chart: string@"nu-complete helm charts" + valuefile: path + --values (-v): any + --namespace (-n): string@"nu-complete kube ns"='test' + --app (-a): string='test' +] { + let values = if ($values | is-empty) { [] } else { [--set-json (record-to-set-json $values)] } + let target = $valuefile | split row '.' | range ..-2 | append [out yaml] | str join '.' + if (not ($target | path exists)) and (([yes no] | input list $'create ($target)?') in [no]) { return } + helm template --debug $app $chart -f $valuefile $values ($namespace | with-flag -n) + | save -f $target +} +