module Cluster class Runner def start SctCore::Helper.ensure_windows_administrator # "sct hostfile" will need it later on existing_cluster = cluster_exists? start_cluster if existing_cluster run_command "kubectl rollout restart -n kube-system deployment registry-creds" run_command "kubectl rollout status -n kube-system deployment registry-creds" else create_secrets enable_addons wait_for_gcr_secret run_command "kubectl apply -f ~/development/spend-cloud/k8s/ingress.yml" wait_for_ingress_ip run_command "kubectl apply -f ~/development/spend-cloud/k8s/dependencies.yml" wait_for_pods run_command "kubectl apply -f ~/development/spend-cloud/k8s/" end post_start end def stop run_command "minikube stop" end def restart SctCore::Helper.ensure_windows_administrator # "sct hostfile" will need it later on stop start end def delete run_command "minikube delete" end def reset SctCore::Helper.ensure_windows_administrator # "sct hostfile" will need it later on delete start end def status print_contexts print_minikube_status if get_minikube_status.find_all { |status| status[1] == 'Stopped' }.count == 0 print_pods_status("kube-system") print_pods_status else UI.important("Please check your minikube status. If all services are stopped you should start the minikube first.") end end def update_config run_command "kubectl config use-context minikube" run_command "kubectl replace -n kube-system -f #{File.expand_path('resources/corefile.yml', __dir__)}" run_command "kubectl rollout restart -n kube-system deployment coredns" run_command "kubectl rollout status -n kube-system deployment coredns" end def apply_deployments run_command "kubectl apply -f ~/development/spend-cloud/k8s/" end def cluster_exists? if system "minikube status", { out: "/dev/null" } # cluster exists and is running return true end if $?.exitstatus == 85 # cluster does not exist return false end # cluster exists but is stopped return true end def start_cluster if SctCore::Helper.operatingSystem == SctCore::Helper::MAC_OS run_command "minikube start --driver=docker --cpus=$(sysctl -n hw.ncpu) --memory=8G" else run_command "minikube start --driver=docker --cpus=$(cat /proc/cpuinfo | grep processor | wc -l) --memory=3G" end update_config end def post_start wait_for_pods copy_proactive_accounts_file run_command "sudo sct hostfile" run_command "minikube tunnel &", { out: "/dev/null", err: "/dev/null" } if SctCore::Helper::is_windows? # leave this running detached forever in the background UI.success("\nāœ”ļø You can visit your environment at šŸ‘‰ https://spend-cloud.spend.cloud.local šŸ‘Œ") end def enable_addons enable_addon "registry-creds" enable_addon "ingress" end def enable_addon(addon) run_command "minikube addons enable #{addon}" deployment = deployments("kube-system").find { |deployment| deployment[:name].include? addon } run_command "kubectl rollout status -n kube-system deployment #{deployment[:name]}" end def wait_for_pods UI.important("Waiting for pods to become ready...") previous_lines = 0 while ! pods.all? { |pod| pod[:ready] } pods_status_lines = get_pods_status.to_s.lines.map { |line| line.chomp } current_lines = pods_status_lines.length line_difference = current_lines - previous_lines if line_difference < 0 # there are now less lines than before line_difference.abs.times do print "\033[1A\033[K" # move the cursor up a line and clear the line end print "\033[#{current_lines}A" # move the cursor all the way up to the start of the table elsif previous_lines > 0 print "\033[#{previous_lines}A" # move the cursor all the way up to the start of the table end pods_status_lines.each do |line| print "#{line}\033[K#{$/}" # print the content of the line, clear remaining characters and add a new line end previous_lines = current_lines sleep 5 end previous_lines.times do print "\033[1A\033[K" # move the cursor up a line and clear the line end UI.success("Pods are now ready.") end def deployments(namespace = "default") output = `kubectl get deployments -n #{namespace}` # split output lines lines = output.split "\n" # exclude first line (table header) lines = lines[1..-1] # get name and status of each pod lines.map do |line| columns = line.split(" ") name = columns[0] { name: name } end end def pods(namespace = "default") output = `kubectl get pods -n #{namespace}` # split output lines lines = output.split "\n" # exclude first line (table header) lines = lines[1..-1] # get name and status of each pod lines.map do |line| columns = line.split(" ") name = columns[0] ready = columns[1].split("/").reduce { |l, r| l == r } replicas = columns[1] status = columns[2] { name: name, ready: ready, replicas: replicas, status: status } end end def create_secrets run_command "kubectl create secret generic gcloud-credentials --from-file=\"$(echo ~)/.config/gcloud/application_default_credentials.json\"" run_command "kubectl create secret generic -n kube-system registry-creds-dpr --from-literal DOCKER_PRIVATE_REGISTRY_PASSWORD=changeme --from-literal DOCKER_PRIVATE_REGISTRY_SERVER=changeme --from-literal DOCKER_PRIVATE_REGISTRY_USER=changeme" run_command "kubectl patch secret -n kube-system registry-creds-dpr -p='{\"metadata\": {\"labels\": { \"app\": \"registry-creds\", \"cloud\": \"dpr\", \"kubernetes.io/minikube-addons\": \"registry-creds\"}}}'" run_command "kubectl create secret generic -n kube-system registry-creds-ecr --from-literal AWS_ACCESS_KEY_ID=changeme --from-literal AWS_SECRET_ACCESS_KEY=changeme --from-literal AWS_SESSION_TOKEN=\"\" --from-literal aws-account=changeme --from-literal aws-assume-role=changeme --from-literal aws-region=changeme" run_command "kubectl patch secret -n kube-system registry-creds-ecr -p='{\"metadata\": {\"labels\": { \"app\": \"registry-creds\", \"cloud\": \"ecr\", \"kubernetes.io/minikube-addons\": \"registry-creds\"}}}'" run_command "kubectl create secret generic -n kube-system registry-creds-gcr --from-file=\"$(echo ~)/.config/gcloud/application_default_credentials.json\" --from-literal=gcrurl=\"https://eu.gcr.io\"" run_command "kubectl patch secret -n kube-system registry-creds-gcr -p='{\"metadata\": {\"labels\": { \"app\": \"registry-creds\", \"cloud\": \"gcr\", \"kubernetes.io/minikube-addons\": \"registry-creds\"}}}'" run_command "kubectl create secret generic -n kube-system registry-creds-acr --from-literal ACR_PASSWORD=changeme --from-literal ACR_CLIENT_ID=changeme --from-literal ACR_URL=changeme" run_command "kubectl patch secret -n kube-system registry-creds-acr -p='{\"metadata\": {\"labels\": { \"app\": \"registry-creds\", \"cloud\": \"acr\", \"kubernetes.io/minikube-addons\": \"registry-creds\"}}}'" end def wait_for_gcr_secret UI.important("Waiting for Google Cloud Registry secret to become available...") while ! `kubectl get secrets`.include? "gcr-secret" sleep 5 end UI.success("Google Cloud Registry secret is now available.") end def wait_for_ingress_ip UI.important("Waiting for ingress IP to become available...") while `kubectl describe ingress | grep "Address" | awk '{print $2}'`.empty? sleep 5 end UI.success("Ingress IP is now available.") end def print_contexts output = `kubectl config get-contexts` lines = output.split "\n" lines = lines[1..-1] rows = lines.map do |line| columns = line.split(" ") current_context = columns[0] == "*" ? "Yes".green : "No".red [columns[2], current_context] end puts Terminal::Table.new title: "Contexts".green, headings: ['Cluster', 'Using context'], rows: rows end def print_minikube_status puts Terminal::Table.new title: "Minikube status".green, headings: ['Name', 'Status'], rows: get_minikube_status end def get_minikube_status output = `minikube status` lines = output.split "\n" rows = lines.map do |line| columns = line.split(" ") [columns[0][0..-2], columns[1]] end end def print_pods_status(namespace = "default") output = get_pods_status(namespace) puts output if output end def get_pods_status(namespace = "default") rows = pods(namespace).map do |pod| status = pod[:status] replicas = pod[:replicas] [ pod[:name], status == "Running" ? status.green : status.red, pod[:ready] ? replicas.green : replicas.red ] end if rows.empty? return end return Terminal::Table.new title: "Pods (namespace: #{namespace})".green, headings: ['Name', 'Status', 'Replicas ready'], rows: rows end def copy_proactive_accounts_file container_name = "proactive-config" pod_id = pods().select {|pod| pod[:name].start_with?(container_name)}.first[:name] begin UI.important("Checking ProActive accounts file...") run_command "kubectl exec #{pod_id} -- test -e /data/proactive_accounts.ini", [:out, :err] => File::NULL rescue => e UI.important("Copying ProActive accounts file to deployments...") src_path = "#{Dir.home()}/development/spend-cloud/k8s/conf/proactive_accounts.ini" command = "kubectl cp #{src_path} #{pod_id}:/data/proactive_accounts.ini -c #{container_name}" run_command command end UI.success("ProActive accounts file is available") end def run_command command, options = {} if ! system command, options raise command.red end end end end