blob: 33f31e0285a1a20069f10b991095739f8689b47b [file]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
import yaml
import re
import json
import time
import os
import requests as req
from subprocess import run
from urllib.parse import urlparse
import uuid
import string
import random
import logging
# takes a string, split in lines and search for the word (a re)
# if field is a number, splits the line in fields separated by spaces and print the selected field
# the output is always space trimmed for easier check
def grep(input, word, field=None, sort=False):
r"""
>>> import openserverless.testutil as tu
>>> tu.grep("a\nb\nc\n", "b")
b
>>> tu.grep(b"a\nb\n c\n", r"a|c")
a
c
>>> tu.grep(b"z\nt\n w\n", r"w|z", sort=True)
w
z
"""
try: input = input.decode()
except: pass
lines = []
for line in str(input).split("\n"):
if re.search(word, line):
line = line.strip()
if not field is None:
try:
line = line.split()[field]
except:
line = "missing-field"
lines.append(line)
if sort:
lines.sort()
res = "\n".join(lines)
print(res)
# print a file
def cat(file):
with open(file, "r") as f:
print(f.read())
# print a file
def fread(file):
with open(file, "r") as f:
return f.read()
# capture and print an exception with its type
# or just print the output of the fuction
def catch(f):
"""
>>> import openserverless.testutil as tu
>>> tu.catch(lambda: "ok")
ok
>>> def error():
... raise Exception("error")
>>> tu.catch(error)
<class 'Exception'> error
"""
try: print(f().strip())
except Exception as e:
print(type(e), str(e).strip())
# print not blank lines only
def nprint(out):
for line in out.split("\n"):
if line.strip() != "":
print(line)
# print in yaml an obj
def yprint(obj):
print(yaml.dump(obj))
# load an YAML file
def load_yaml(file):
f = open(file)
l = list(yaml.load_all(f, yaml.Loader))
if len(l) > 0:
return l[0]
return {}
# mocking and spying kube support
class MockKube:
"""
>>> from openserverless.testutil import *
>>> m = MockKube()
>>> m.invoke()
>>> m.config("", "ok")
>>> m.invoke()
'ok'
>>> m = MockKube()
>>> m.config("apply", "applied")
>>> m.invoke()
>>> m.echo()
>>> m.invoke("apply", "-f")
kubectl apply -f
'applied'
>>> m.peek()
'apply -f'
>>> m.dump()
''
>>> m.save("hello")
>>> m.dump()
'hello'
"""
def __init__(self):
self.reset()
def reset(self):
self.map = {}
self.queue = []
self.saved = []
self.echoFlag = False
self.enabled = False
def echo(self, flag=True):
self.echoFlag = flag
def peek(self, index=-1):
res = self.queue[index][0]
return res
def dump(self, index=-1):
return self.queue[index][1]
def save(self, data, index=-1):
self.queue[index] = (self.queue[index][0], data)
def config(self, request, response):
self.enabled = True
self.map[request] = response
def invoke(self, *args):
if self.enabled:
cmd = " ".join(args)
for key in list(self.map.keys()):
if cmd.startswith(key):
if self.echoFlag:
print("kubectl", cmd)
self.queue.append( (cmd,"") )
return self.map[key]
return None
def load_sample_config(name="whisk"):
with open(f"tests/{name}.yaml") as f:
c = yaml.safe_load(f)
return c['spec']
# read environment variables from Dockerfile, .env and git config
def load_image_env():
# operator images defaults in Dockerfile, can be overriden in .env
r = run('grep "ARG OPERATOR_IMAGE_DEFAULT=" Dockerfile', shell=True, capture_output=True)
opimg = r.stdout.strip().decode("ascii").split("=")[-1]
r = run('grep MY_OPERATOR_IMAGE .env', shell=True, capture_output=True)
if r.returncode == 0:
opimg = r.stdout.strip().decode("ascii").split("=")[-1]
g = run('grep GITHUB_USER= .env', shell=True, capture_output=True)
if g.returncode == 0 and opimg:
ghuser = g.stdout.strip().decode("ascii").split("=")[-1]
opimg = opimg.replace("${GITHUB_USER}",ghuser)
# gives precedence to env variable if set
os.environ["OPERATOR_IMAGE"] = os.getenv("MY_OPERATOR_IMAGE", opimg)
# operator tag is the git tag of the operator
r = run('git describe --tags --abbrev=0 2>/dev/null || git rev-parse --short HEAD', shell=True, capture_output=True)
tag = r.stdout.strip().decode("ascii")
os.environ["OPERATOR_TAG"] = tag
# controller images and tag are in the Dockerfile
r = run("grep -Po '(?<=CONTROLLER_IMAGE=).*' Dockerfile", shell=True, capture_output=True)
os.environ["CONTROLLER_IMAGE"] = r.stdout.decode("ascii").strip()
r = run("grep -Po '(?<=CONTROLLER_TAG=).*' Dockerfile", shell=True, capture_output=True)
os.environ["CONTROLLER_TAG"] = r.stdout.decode("ascii").strip()
def set_apihost_from_kubeconfig(cfg):
# assume for k3s and microk8s,
# the openserverless apihost is the same as the kube api server
r = run("kubectl config view -o json | jq -r '.clusters[0].cluster.server'", shell=True, capture_output=True)
server = r.stdout.decode("ascii").strip()
hostname = urlparse(server).hostname
kube = cfg.get("openserverless.kube")
if kube is None:
return
if kube in ["k3s", "microk8s"]:
cfg.put("openserverless.apihost", hostname)
if kube == "openshift":
hostname1 = ".".join(["openserverless", "apps"] + hostname.split(".")[1:])
cfg.put("openserverless.apihost", hostname1)
def json2flatdict(data):
return dict(flatdict.FlatterDict(json.loads(data), delimiter="."))
def get_by_key_sub(dic, key):
res = []
for k in list(dic.keys()):
try:
k.index(key)
res.append(dic[k])
except:
pass
return "\n".join(res)
def read_dotenv():
import os
try:
f = open(".env")
lines = f.readlines()
#print(lines)
for line in lines:
#print(line)
#line = lines[1]
a = line.split("=", 1)
if len(a) == 2:
print(a[0])
os.environ[a[0]] = a[1].strip()
f.close()
except Exception as e:
print(e)
print(".env not found")
pass
def get_with_retry(url, max_seconds):
start = time.time()
delta = 0
while delta < max_seconds:
try:
r = req.get(url, timeout=1)
if r.status_code == 200:
return r.text
except Exception as e:
print(e)
print(f"waiting since: {delta} seconds")
delta = int(time.time() - start)
time.sleep(1)
return ""
# retry a function until it returns a given value
# return true when the value is what is expected, false otherwise
def retry(fn, value, max=10, delay=1):
for i in range(0, max):
if fn() == value:
return True
time.sleep(delay)
print(i, "retrying...")
return False
def generate_ow_uid():
"""
>>> import openserverless.testutil as util
>>> len(util.generate_ow_uid())
36
"""
return str(uuid.uuid4())
def generate_ow_key():
"""
>>> import openserverless.testutil as util
>>> len(util.generate_ow_key())
64
"""
return ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(64))
def generate_ow_auth():
"""
>>> import openserverless.testutil as util
>>> len(util.generate_ow_auth())
101
"""
uid = generate_ow_uid()
key = generate_ow_key()
return f"{uid}:{key}"
def load_sample_user_config(name="whisk-user"):
with open(f"tests/{name}.yaml") as f:
c = yaml.safe_load(f)
return c['spec']
def load_sample_runtimes(name="runtimes"):
with open(f"tests/{name}.json") as f:
return json.load(f)
def enable_debug_logging():
logging.basicConfig(level=logging.DEBUG)
def run_proc(cmd):
try:
get_ipython().system(cmd)
except NameError:
import subprocess
subprocess.run(cmd.split(), check=True)