Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Verify Kubernetes context before install #1739

Merged
merged 21 commits into from
Oct 13, 2022
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions Backend/Services/LiftService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,8 @@ public async Task<string> LiftExport(
x => activeWords.All(w => w.Guid != x.Guid)).DistinctBy(w => w.Guid).ToList();
foreach (var wordEntry in activeWords)
{
var entry = new LexEntry(MakeSafeXmlAttribute(wordEntry.Vernacular), wordEntry.Guid);
var id = MakeSafeXmlAttribute(wordEntry.Vernacular) + "_" + wordEntry.Guid;
var entry = new LexEntry(id, wordEntry.Guid);
if (DateTime.TryParse(wordEntry.Created, out var createdTime))
{
entry.CreationTime = createdTime;
Expand All @@ -248,7 +249,8 @@ public async Task<string> LiftExport(

foreach (var wordEntry in deletedWords)
{
var entry = new LexEntry(MakeSafeXmlAttribute(wordEntry.Vernacular), wordEntry.Guid);
var id = MakeSafeXmlAttribute(wordEntry.Vernacular) + "_" + wordEntry.Guid;
var entry = new LexEntry(id, wordEntry.Guid);

AddNote(entry, wordEntry);
AddVern(entry, wordEntry, vernacularBcp47);
Expand Down Expand Up @@ -488,7 +490,7 @@ private static void LdmlExport(string filePath, string vernacularBcp47, List<str
/// </summary>
/// <param name="sInput"></param>
/// <returns></returns>
public static string? MakeSafeXmlAttribute(string sInput)
public static string MakeSafeXmlAttribute(string sInput)
{
return SecurityElement.Escape(sInput);
}
Expand Down
92 changes: 92 additions & 0 deletions deploy/scripts/aws_env.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#! /usr/bin/env python3
"""Set AWS Environment variables from aws cli profiles."""

from __future__ import annotations

import os
import re
from typing import Dict, List, Optional

from utils import choose_from_list, run_cmd


def aws_version() -> Optional[int]:
"""Test if the aws cli version 2 is installed."""
try:
result = run_cmd(["aws", "--version"], check_results=False, chomp=True)
except FileNotFoundError:
print("AWS CLI version 2 is not installed.")
return None
else:
if result.returncode == 0:
# get major version number from stdout
match = re.match(r"aws-cli/(\d+)\..*", result.stdout)
if match:
return int(match.group(1))
return None


def list_aws_profiles() -> List[str]:
aws_ver = aws_version()
if aws_ver is not None and aws_ver == 2:
result = run_cmd(["aws", "configure", "list-profiles"], chomp=True)
return result.stdout.split("\n")
return []


def get_profile_var(profile: str, var_name: str) -> str:
result = run_cmd(["aws", "configure", "--profile", profile, "get", var_name], chomp=True)
return result.stdout


def init_aws_environment() -> None:
profile_list = list_aws_profiles()
# Build a map for looking up a profile name from the access key id. This
# algorithm assumes:
# - the 'default' profile will be processed first
# - if there are profiles using the same access key id, only the last
# one will be put into the map
if len(profile_list) == 0:
return
profile_map: Dict[str, str] = {}
for profile in profile_list:
key_id = get_profile_var(profile, "aws_access_key_id")
profile_map[key_id] = profile
curr_access_key = os.getenv("AWS_ACCESS_KEY_ID", "")
if curr_access_key in profile_map:
curr_profile = profile_map[curr_access_key]
else:
curr_profile = None
aws_profile = choose_from_list("AWS Environment", curr_profile, profile_list)
if aws_profile is not None and aws_profile != curr_profile:
os.environ["AWS_PROFILE"] = aws_profile
os.environ["AWS_ACCESS_KEY_ID"] = get_profile_var(aws_profile, "aws_access_key_id")
os.environ["AWS_SECRET_ACCESS_KEY"] = get_profile_var(aws_profile, "aws_secret_access_key")
os.environ["AWS_DEFAULT_REGION"] = get_profile_var(aws_profile, "region")
result = run_cmd(
[
"aws",
"sts",
"--profile",
aws_profile,
"get-caller-identity",
"--query",
"Account",
"--output",
"text",
],
chomp=True,
)
os.environ["AWS_ACCOUNT"] = result.stdout


if __name__ == "__main__":
init_aws_environment()
print("AWS Environment:")
for env_var in [
"AWS_ACCOUNT",
"AWS_DEFAULT_REGION",
"AWS_ACCESS_KEY_ID",
"AWS_SECRET_ACCESS_KEY",
]:
print(f"{env_var}: {os.getenv(env_var, None)}")
103 changes: 103 additions & 0 deletions deploy/scripts/kube_env.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
#!/usr/bin/env python3
"""Manage the Kubernetes environment for kubectl & helm."""

from __future__ import annotations

import argparse
from typing import List, Optional

from utils import choose_from_list, run_cmd


class KubernetesEnvironment:
def __init__(self, args: argparse.Namespace) -> None:
if "kubeconfig" in args and args.kubeconfig is not None:
self.kubeconfig = args.kubeconfig
else:
self.kubeconfig = None
if "context" in args and args.context is not None:
# if the user specified a context, use that one.
self.kubecontext = args.context
else:
context_list: List[str] = []

result = run_cmd(
["kubectl"] + self.get_kubeconfig() + ["config", "get-contexts", "--no-headers"],
check_results=True,
)
curr_context: Optional[str] = None
for line in result.stdout.splitlines():
if line[0] == "*":
curr_context = line.split()[1]
context_list.append(curr_context)
else:
context_list.append(line.split()[0])

# If there is more than one context available, prompt the user to make sure
# that the intended context will be used.
curr_context = choose_from_list("context", curr_context, context_list)
if curr_context:
self.kubecontext = curr_context
else:
self.kubecontext = None
if "debug" in args:
self.debug = args.debug
else:
self.debug = False

def get_kubeconfig(self) -> List[str]:
if self.kubeconfig is not None:
return ["--kubeconfig", self.kubeconfig]
return []

def get_helm_opts(self) -> List[str]:
"""
Create list of general helm options.
"""
helm_opts = self.get_kubeconfig()

if self.kubecontext is not None:
helm_opts.extend(["--kube-context", self.kubecontext])
if self.debug:
helm_opts.append("--debug")
return helm_opts

def get_kubectl_opts(self) -> List[str]:
"""
Create list of general kubectl options.
"""
kubectl_opts = self.get_kubeconfig()

if self.kubecontext is not None:
kubectl_opts.extend(["--context", self.kubecontext])
return kubectl_opts


def add_kube_opts(parser: argparse.ArgumentParser) -> None:
"""Add commandline arguments for KUbernetes tools."""
parser.add_argument(
"--context",
help="Context in kubectl configuration file to be used.",
)
parser.add_argument(
"--debug",
action="store_true",
help="Enable debugging output for helm commands.",
)
parser.add_argument(
"--kubeconfig",
help="Specify the kubectl configuration file to be used.",
)


if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Generate Helm Charts for The Combine.",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
add_kube_opts(parser)
args = parser.parse_args()

kube_env = KubernetesEnvironment(args)
print(f"kubectl {kube_env.get_kubectl_opts()} ...")
print(f"helm {kube_env.get_helm_opts()} ...")
11 changes: 7 additions & 4 deletions deploy/scripts/setup_cluster.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
from typing import Any, Dict, List

from enum_types import ExitStatus, HelmAction
from utils import add_helm_opts, add_namespace, get_helm_opts, run_cmd
from kube_env import KubernetesEnvironment, add_kube_opts
from utils import add_namespace, run_cmd
import yaml

scripts_dir = Path(__file__).resolve().parent
Expand All @@ -24,7 +25,7 @@ def parse_args() -> argparse.Namespace:
description="Build containerd container images for project.",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
add_helm_opts(parser)
add_kube_opts(parser)
parser.add_argument(
"--type",
"-t",
Expand Down Expand Up @@ -90,13 +91,15 @@ def main() -> None:
for chart in yaml.safe_load(chart_list_results.stdout):
curr_charts.append(chart["name"])

# Verify the Kubernetes/Helm environment
kube_env = KubernetesEnvironment(args)
# Install the required charts
for chart_descr in this_cluster:
chart_spec = config[chart_descr]["chart"]
# add namespace if needed
add_namespace(chart_spec["namespace"])
add_namespace(chart_spec["namespace"], kube_env.get_kubectl_opts())
# install the chart
helm_cmd = ["helm"] + get_helm_opts(args)
helm_cmd = ["helm"] + kube_env.get_helm_opts()
if chart_spec["name"] in curr_charts:
helm_action = HelmAction.UPGRADE
else:
Expand Down
16 changes: 11 additions & 5 deletions deploy/scripts/setup_combine.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@
from typing import Any, Dict, List

from app_release import get_release
from aws_env import init_aws_environment
import combine_charts
from enum_types import ExitStatus, HelmAction
from utils import add_helm_opts, add_namespace, get_helm_opts, run_cmd
from kube_env import KubernetesEnvironment, add_kube_opts
from utils import add_namespace, run_cmd
import yaml

scripts_dir = Path(__file__).resolve().parent
Expand All @@ -41,7 +43,7 @@ def parse_args() -> argparse.Namespace:
description="Generate Helm Charts for The Combine.",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
add_helm_opts(parser)
add_kube_opts(parser)
parser.add_argument(
"--clean", action="store_true", help="Delete chart, if it exists, before installing."
)
Expand Down Expand Up @@ -213,9 +215,13 @@ def main() -> None:
else:
profile = args.profile

# Create a base helm command for commands used to alter
# Verify the Kubernetes/Helm environment
kube_env = KubernetesEnvironment(args)
# Cache options for helm commands used to alter
# the target cluster
helm_opts = get_helm_opts(args)
helm_opts = kube_env.get_helm_opts()
# Check AWS Environment Variables
init_aws_environment()

# create list of target specific variable values
target_vars = [
Expand All @@ -242,7 +248,7 @@ def main() -> None:
for chart in config["profiles"][profile]["charts"]:
# create the chart namespace if it does not exist
chart_namespace = config["charts"][chart]["namespace"]
if add_namespace(chart_namespace):
if add_namespace(chart_namespace, kube_env.get_kubectl_opts()):
installed_charts: List[str] = []
else:
# get list of charts in target namespace
Expand Down
2 changes: 1 addition & 1 deletion deploy/scripts/setup_files/combine_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ targets:
# override values for 'thecombine' chart
thecombine:
global:
serverName: thecombine.local
serverName: thecombine.localhost
nuc1:
profile: nuc
env_vars_required: true
Expand Down
Loading