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

[WIP] Automate Kube Creation and Deployment #6

Closed
wants to merge 16 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,4 @@ log
report.html
.hlint.yaml
*.svg
*.pem
182 changes: 156 additions & 26 deletions scripts/kubernetes/bootstrap_deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,53 @@
import yaml

from kubernetes import client, config
from kubernetes.client import configuration


NAMESPACE = "default"
DEPLOYMENT_NAME = "chainweb"
DEPLOYMENT_IMAGE = "kadena/chainweb-bootstrap-node:v0"
DNS_NAME = ".chainweb.com."
DNS_NAME = ".chainweb.com"
PORT_NUMBER = 1789
PER_REGION_NODES = 1
PER_CLUSTER_NODES = 1

# --- SECRETS
# Adds bootstrap config file as a secret volume.


def create_secret_from_file(api_instance, filename):
data = ""
def create_secret_from_file(api_instance, configPath, certPath, keyPath):
config = ""
with open(configPath, 'r') as f:
config = f.read()

data = {
"node.config": config,
}

# if certificate file provided
if certPath:
cert = ""
with open(certPath, 'r') as f:
cert = f.read()

certYaml = dict(
chainweb=dict(p2p=dict(peer=dict(certificateChain=cert))))
data["cert.config"] = str(
yaml.dump(certYaml, default_flow_style=False, default_style="|"))

# if certificate private key file provided
if keyPath:
key = ""
with open(keyPath, 'r') as f:
key = f.read()

with open(filename, 'r') as f:
data = f.read()
keyYaml = dict(chainweb=dict(p2p=dict(peer=dict(key=key))))
data["privkey.config"] = str(
yaml.dump(keyYaml, default_flow_style=False, default_style="|"))

secret = client.V1Secret(
type="Opaque",
metadata=client.V1ObjectMeta(name="bootstrap-config"),
string_data={"test-bootstrap-node.config": data})
string_data=data)

api_instance.create_namespaced_secret(
namespace=NAMESPACE, body=secret, pretty='true')
Expand Down Expand Up @@ -149,10 +174,29 @@ def create_pod_template_with_pvc(args):
# Configureate Pod template container
isTTY = args.image == "chainweb-base" # otherwise container will always "finish" and restart.
pull_policy = "Never" if args.local else "Always"
command = [
"/bin/chainweb-node", "--config-file=/tmp/node.config",
"--p2p-max-session-count=4",
"--telemetry-log-handle=es:search-tnlogs-eb57oi7cimmanlguprazntbqva.us-east-1.es.amazonaws.com:80",
"--log-handle=es:search-tnlogs-eb57oi7cimmanlguprazntbqva.us-east-1.es.amazonaws.com:80",
"--disable-transaction-index"
]

# if certificate file was present
if args.certificate:
command.append("--config-file=/tmp/cert.config")
# if certificate private key was present
if args.privateKey:
command.append("--config-file=/tmp/privkey.config")

container = client.V1Container(
name="chainweb",
image=args.image,
command=command,
resources=client.V1ResourceRequirements(requests={
"cpu": "400m",
"memory": "14Gi"
}),
image_pull_policy=pull_policy,
tty=isTTY,
ports=[client.V1ContainerPort(container_port=PORT_NUMBER)],
Expand All @@ -173,7 +217,7 @@ def create_pod_template_with_pvc(args):
def create_stateful_set_obj(args):
spec = client.V1beta2StatefulSetSpec(
pod_management_policy="Parallel",
replicas=PER_REGION_NODES,
replicas=PER_CLUSTER_NODES,
selector=client.V1LabelSelector(match_labels={"app": "chainweb"}),
service_name="chainweb-service",
template=create_pod_template_with_pvc(args),
Expand Down Expand Up @@ -219,61 +263,147 @@ def arg_parsing():

create_p.add_argument(
"--image",
default="kadena/chainweb-bootstrap-node:v0",
default="kadena/chainweb-bootstrap-node:latest",
help="A docker image to run")

create_p.add_argument(
"--subDomain",
default="testing",
help="Sub-domain name for application to run externally")

create_p.add_argument(
"--local",
action="store_true",
help="Assume the docker image can be found locally")

create_p.add_argument(
"--logToElasticSearch",
action="store_true",
help="Sends logs to elastic search logger")

create_p.add_argument(
"--context",
help="kubectl config context to use")

create_p.add_argument("--certificate", help="Filename of CA certificate.")

create_p.add_argument(
"--privateKey", help="Filename of CA certificate private key.")

# TODO make a mandatory argument for `create`
create_p.add_argument(
"--nodeConfig", help="Filename of chainweb node config.")

create_p.set_defaults(func=create_resources)

# --- `delete` flags --- #
delete_p = subparsers.add_parser(
"delete", help="Tear down a Chainweb node cluster")

delete_p.add_argument("--context", help="kubectl config context to use")

delete_p.set_defaults(func=delete_resources)

# --- `update` flags --- #
update_p = subparsers.add_parser(
"update", help="Update Chainweb node deployment component(s)")

update_p.add_argument(
"--image",
default="kadena/chainweb-bootstrap-node:latest",
help="A docker image to update deployment with")

update_p.add_argument("--context", help="kubectl config context to use")

update_p.set_defaults(func=update_resources)

return parser.parse_args()


def main():
args = arg_parsing()

# Ask user which context (i.e. cluster) they want to work with
contexts, _ = config.list_kube_config_contexts()
contexts, active_context = config.list_kube_config_contexts()

if not contexts:
print("Cannot find any context in kube-config file.")
return

config.load_kube_config()
contexts = [context['name'] for context in contexts]
active_index = contexts.index(active_context['name'])

if args.context:
new_index = contexts.index(args.context)
option = contexts[new_index]
config.load_kube_config(context=option)

print("Active host is %s" % option)

print("Running with: ", args)
args.func(args, option)

print("Done")
else:
option = contexts[active_index]
config.load_kube_config(context=option)

print("Running with: ", args)
args.func(args)
print("Active host is %s" % option)

print("Done")
print("Running with: ", args)
args.func(args, option)

print("Done")

def create_resources(args):




def create_resources(args, context):
print("Creating cluster...")

apps_v1beta2 = client.AppsV1beta2Api()
core_v1 = client.CoreV1Api()
apps_v1beta2 = client.AppsV1beta2Api(
api_client=config.new_client_from_config(context=context))
core_v1 = client.CoreV1Api(
api_client=config.new_client_from_config(context=context))

create_secret_from_file(core_v1,
"scripts/kubernetes/kube-bootstrap-node.config")
create_secret_from_file(core_v1, args.nodeConfig, args.certificate,
args.privateKey)
create_headless_service(core_v1)
create_pod_service(core_v1, "us1", "chainweb-0")
create_pod_service(core_v1, args.subDomain, "chainweb-0")
create_stateful_set(apps_v1beta2, args)


def delete_resources(args):
def update_resources(args, context):
print("Updating cluster...")

apps_v1beta2 = client.AppsV1beta2Api(
api_client=config.new_client_from_config(context=context))

stateful_set_patch = {
"spec": {
"template": {
"spec": {
"containers": [{
"name": "chainweb",
"image": args.image
}]
}
}
}
}

api_response = apps_v1beta2.patch_namespaced_stateful_set(
DEPLOYMENT_NAME, NAMESPACE, stateful_set_patch, pretty=True)
print("Container image updated. status='%s'" % str(api_response.status))


def delete_resources(args, context):
print("Deleting cluster...")

apps_v1beta2 = client.AppsV1beta2Api()
core_v1 = client.CoreV1Api()
apps_v1beta2 = client.AppsV1beta2Api(
api_client=config.new_client_from_config(context=context))
core_v1 = client.CoreV1Api(
api_client=config.new_client_from_config(context=context))

try_delete(delete_stateful_set, apps_v1beta2)
try_delete(delete_secret, core_v1)
Expand All @@ -294,4 +424,4 @@ def try_delete(delete_func, api_instance, arg=None):


if __name__ == '__main__':
main()
main()
6 changes: 3 additions & 3 deletions scripts/kubernetes/dev_startup.sh
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
#!/bin/bash

# Start minikube
eval `opam config env`
# Start minikubeeval `opam config env`

minikube delete &&
minikube start


# Save local docker images into minishift cluster
# Source: https://blogmilind.wordpress.com/2018/01/30/running-local-docker-images-in-kubernetes/
docker save chainweb-base > "chainweb-base.tar"
docker save chainweb-base > "chainweb-base.tar"
docker save chainweb-bootstrap-node > "chainweb-bootstrap-node.tar"

eval $(minikube docker-env)
Expand Down
36 changes: 36 additions & 0 deletions scripts/kubernetes/get_certificate_configs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from os import path

import sys
import yaml


def create_certificate_configs(certPath, keyPath):
cert = ""
with open(certPath, 'r') as f:
cert = f.read()

certYaml = dict(
chainweb=dict(p2p=dict(peer=dict(certificateChain=cert))))

with open('fullchain.config', 'w') as outfile:
yaml.dump(certYaml, outfile, default_flow_style=False, default_style="|")

key = ""
with open(keyPath, 'r') as f:
key = f.read()

keyYaml = dict(chainweb=dict(p2p=dict(peer=dict(key=key))))

with open('privkey.config', 'w') as outfile:
yaml.dump(
keyYaml, outfile, default_flow_style=False, default_style="|")



def main():
print(sys.argv[1])
#create_certificate_configs(sys.argv[1], sys.argv[2])


if __name__ == '__main__':
main()
2 changes: 1 addition & 1 deletion scripts/kubernetes/kube-bootstrap-node.config
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ chainweb:
QM/ThFn9xv3RUppF4aGrnfCsldXfrxmwgLvOw3qxLOCk6mHOcInRjw4Qdpk=
-----END PRIVATE KEY-----
interface: '*'
certificate: |
certificateChain: |
-----BEGIN CERTIFICATE-----
MIIFBDCCAuygAwIBAgIBATANBgkqhkiG9w0BAQ0FADAUMRIwEAYDVQQDDAlsb2Nh
bGhvc3QwHhcNMTgxMjIyMDM1NzM2WhcNMzAwMzEwMDM1NzM2WjAUMRIwEAYDVQQD
Expand Down
Loading