require 'sct/command_interface' require 'terminal-table' module Sct class ClusterCommand IS_PUBLIC_COMMAND = true SYNTAX = 'sct cluster up | sct cluster down | sct cluster setup | sct cluster reset | sct cluster update-config | sct cluster delete-stalled-pods' SUMMARY = 'Perform actions on the minikube cluster.' EXAMPLE = 'sct cluster up | sct cluster down | sct cluster setup | sct cluster reset | sct cluster update-config | sct cluster delete-stalled-pods' EXAMPLE_DESCRIPTION = 'Perform actions on the minikube cluster.' DESCRIPTION = "sct cluster allows you to start, stop, setup/reset, update the config, or delete the stalled pods of the Spend Cloud minikube cluster." OPTIONS = [] def execute(args, options) return puts "SCT has not been initialized. Run 'sct init' first.".red unless Sct::Config.exists case args[0] when "up" up when "down" down when "update-config" update_config when "setup", "reset" reset when "delete-stalled-pods" delete_stalled_pods(feedback: true) when "status" status else puts "Unknown or missing argument. Please run 'sct cluster up', 'sct cluster down', 'sct cluster setup', 'sct cluster reset', 'sct cluster update-config', or 'sct cluster delete-stalled-pods'.".red end end def up start run_command "kubectl delete pod -n kube-system #{pods("kube-system").map { |pod| pod[:name] if pod[:name].start_with? "registry-creds" } .compact.join(" ")}" run_command "kubectl rollout status -n kube-system deployment/registry-creds" post_start end def down run_command "#{minikube} stop" end def reset run_command "#{minikube} delete" start create_secrets run_command "#{minikube} addons enable registry-creds" run_command "#{minikube} addons enable ingress" run_command "kubectl rollout status -n kube-system deployment/registry-creds" run_command "kubectl rollout status -n kube-system deployment/nginx-ingress-controller" 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/" post_start end def update_config if Sct::Helpers.operatingSystem == Sct::Helpers::WINDOWS windows_home_path = Sct::Helpers.windowsHomePath kube_file_path = windows_home_path+"/.kube/config" if !File.exists?(kube_file_path) return puts "#{kube_file_path} doesn't exist".red end run_command "sed -e 's~\\\\~/~g' -e 's~C:~/mnt/c~g' < #{kube_file_path} > ~/.kube/minikube-config" puts "#{kube_file_path} copied to ~/.kube/minikube-config".green end 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 delete pod -n kube-system #{pods("kube-system").map { |pod| pod[:name] if pod[:name].start_with? "coredns" } .compact.join(" ")}" run_command "kubectl rollout status -n kube-system deployment/coredns" end def delete_stalled_pods(feedback: false) stalled_pods = pods.select { |pod| pod[:stalled] } if stalled_pods.empty? puts "There are no stalled pods.".green if feedback else run_command "kubectl delete pods #{stalled_pods.map { |pod| pod[:name] } .join(" ")}" end end def start if Sct::Helpers.operatingSystem == Sct::Helpers::MAC_OS run_command "#{minikube} start --cpus=$(sysctl -n hw.ncpu) --memory=8G" else run_command "#{minikube} start --cpus=$(cat /proc/cpuinfo | grep processor | wc -l) --memory=10G" end run_command "#{minikube} ssh -- 'sudo su -c \"echo 10048576 > /proc/sys/fs/inotify/max_user_watches\"'" update_config end def post_start wait_for_pods run_command "sudo sct hostfile" puts "\nāœ”ļø You can visit your environment at šŸ‘‰ https://spend-cloud.spend.cloud.local šŸ‘Œ" 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 puts "Waiting for Google Cloud Registry secret to become available...".yellow while ! `kubectl get secrets`.include? "gcr-secret" sleep 5 end puts "Google Cloud Registry secret is now available.".green end def wait_for_ingress_ip puts "Waiting for ingress IP to become available...".yellow while `kubectl describe ingress | grep "Address" | awk '{print $2}'`.empty? sleep 5 end puts "Ingress IP is now available.".green end def wait_for_pods puts "Waiting for pods to become ready...".yellow while ! pods.all? { |pod| pod[:status] == "Running" } delete_stalled_pods sleep 5 end puts "Pods are now ready.".green end def status minikube_status pods_status system_pods current_contexts end def pods_status rows = [] pods.map do |pod| status = pod[:status] == "Running" ? pod[:status].green : pod[:status].red rows << [pod[:name], status] end puts Terminal::Table.new :title => "Pods Status".green, :headings => ['Name', 'Status'], :rows => rows end def system_pods rows = [] pods("kube-system").map do |pod| status = pod[:status] == "Running" ? pod[:status].green : pod[:status].red rows << [pod[:name], status] end puts Terminal::Table.new :title => "System pods Status".green, :headings => ['Name', 'Status'], :rows => rows end def current_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 rows << [columns[2], current_context] end puts Terminal::Table.new :title => "Contexts".green, :headings => ['Cluster', 'Using context'], :rows => rows end def minikube_status output = `#{minikube} status` lines = output.split "\n" rows = [] lines.map do |line| columns = line.split(" ") rows << [columns[0], columns[1]] end puts Terminal::Table.new :title => "Minikube Status".green, :headings => ['Name', 'Status'], :rows => rows end def pods(namespace = nil) if namespace output = `kubectl get pods -n #{namespace}` else output = `kubectl get pods` end # 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] status = columns[2] stalled = status == "ErrImagePull" || status == "ImagePullBackOff" { name: name, status: status, stalled: stalled } end end def run_command command if ! system command raise command.red end end def minikube if Sct::Helpers.operatingSystem == Sct::Helpers::WINDOWS return "minikube.exe" else return "minikube" end end implements CommandInterface end end