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

Make ETOS API compatible with the kubernetes controller poc #69

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
3f105ca
Make ETOS API compatible with the kubernetes controller poc
t-persson Jul 22, 2024
71032a7
Fix the role
t-persson Jul 22, 2024
ce35281
Disable kubernetes config loading when testing
t-persson Jul 23, 2024
cf85e7e
Pass on cluster name to testrun
t-persson Jul 23, 2024
eefce42
Environment image and pull policy
t-persson Jul 24, 2024
b313647
Send activity triggered from ETOS API
t-persson Jul 24, 2024
b73fc0e
Add an endpoint for getting sub suite definition
t-persson Jul 24, 2024
60e1d7c
Change url to uri to match the TERCC specification
t-persson Jul 26, 2024
99be540
Kubernetes from etos library
t-persson Jul 26, 2024
006565e
Don't send activity triggered
t-persson Jul 26, 2024
58dc188
Raise a proper exception
t-persson Jul 26, 2024
994f9d8
Fixes for tox
t-persson Jul 26, 2024
92e0f8e
Rename test environment variable
t-persson Jul 26, 2024
42ddcd7
Fix dataset and use Environment client for subsuite
t-persson Jul 26, 2024
4230e46
Add log listener image to testrun spec
t-persson Jul 29, 2024
7863482
Set strict
t-persson Jul 30, 2024
2612024
Stop sending TERCC and pass test suite URL to testrun
t-persson Aug 1, 2024
4ac8db9
Set the testrun name as the first suite in tercc
t-persson Aug 1, 2024
9802e5b
Add testrunner version to testrun
t-persson Aug 15, 2024
430e64a
Add ETR version
t-persson Aug 19, 2024
31632c3
Fix abort
t-persson Aug 19, 2024
46fbee0
Pass retention to testrun if set
t-persson Aug 26, 2024
aebca8a
Remove unused suiteSource
t-persson Oct 17, 2024
dca70d1
Update to the merged version of etos lib
t-persson Oct 17, 2024
ca95512
Fix review comments
t-persson Oct 23, 2024
10ecff4
Update ETOS library version
t-persson Oct 23, 2024
f858232
Fix after rebase
t-persson Oct 23, 2024
4f0128f
Update configs
t-persson Oct 23, 2024
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: 8 additions & 0 deletions internal/configs/base/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
type Config interface {
ServiceHost() string
ServicePort() string
StripPrefix() string
LogLevel() string
LogFilePath() string
ETOSNamespace() string
Expand All @@ -35,6 +36,7 @@ type Config interface {
type cfg struct {
serviceHost string
servicePort string
stripPrefix string
logLevel string
logFilePath string
etosNamespace string
Expand All @@ -48,6 +50,7 @@ func Get() Config {

flag.StringVar(&conf.serviceHost, "address", EnvOrDefault("SERVICE_HOST", "127.0.0.1"), "Address to serve API on")
flag.StringVar(&conf.servicePort, "port", EnvOrDefault("SERVICE_PORT", "8080"), "Port to serve API on")
flag.StringVar(&conf.stripPrefix, "stripprefix", EnvOrDefault("STRIP_PREFIX", ""), "Strip a URL prefix. Useful when a reverse proxy sets a subpath. I.e. reverse proxy sets /stream as prefix, making the etos API available at /stream/v1/events. In that case we want to set stripprefix to /stream")
flag.StringVar(&conf.logLevel, "loglevel", EnvOrDefault("LOGLEVEL", "INFO"), "Log level (TRACE, DEBUG, INFO, WARNING, ERROR, FATAL, PANIC).")
flag.StringVar(&conf.logFilePath, "logfilepath", os.Getenv("LOG_FILE_PATH"), "Path, including filename, for the log files to create.")
flag.StringVar(&conf.etosNamespace, "etosnamespace", ReadNamespaceOrEnv("ETOS_NAMESPACE"), "Path, including filename, for the log files to create.")
Expand All @@ -68,6 +71,11 @@ func (c *cfg) ServicePort() string {
return c.servicePort
}

// StripPrefix returns the prefix to strip. Empty string if no prefix.
func (c *cfg) StripPrefix() string {
return c.stripPrefix
}

// LogLevel returns the log level.
func (c *cfg) LogLevel() string {
return c.logLevel
Expand Down
8 changes: 8 additions & 0 deletions internal/configs/iut/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
type Config interface {
ServiceHost() string
ServicePort() string
StripPrefix() string
LogLevel() string
LogFilePath() string
ETOSNamespace() string
Expand All @@ -35,6 +36,7 @@ type Config interface {
type cfg struct {
serviceHost string
servicePort string
stripPrefix string
logLevel string
logFilePath string
etosNamespace string
Expand All @@ -48,6 +50,7 @@ func Get() Config {

flag.StringVar(&conf.serviceHost, "address", EnvOrDefault("SERVICE_HOST", "127.0.0.1"), "Address to serve API on")
flag.StringVar(&conf.servicePort, "port", EnvOrDefault("SERVICE_PORT", "8080"), "Port to serve API on")
flag.StringVar(&conf.stripPrefix, "stripprefix", EnvOrDefault("STRIP_PREFIX", ""), "Strip a URL prefix. Useful when a reverse proxy sets a subpath. I.e. reverse proxy sets /stream as prefix, making the etos API available at /stream/v1/events. In that case we want to set stripprefix to /stream")
flag.StringVar(&conf.logLevel, "loglevel", EnvOrDefault("LOGLEVEL", "INFO"), "Log level (TRACE, DEBUG, INFO, WARNING, ERROR, FATAL, PANIC).")
flag.StringVar(&conf.logFilePath, "logfilepath", os.Getenv("LOG_FILE_PATH"), "Path, including filename, for the log files to create.")
flag.StringVar(&conf.databaseHost, "database_host", EnvOrDefault("ETOS_ETCD_HOST", "etcd-client"), "Host to ETOS database")
Expand All @@ -67,6 +70,11 @@ func (c *cfg) ServicePort() string {
return c.servicePort
}

// StripPrefix returns the prefix to strip. Empty string if no prefix.
func (c *cfg) StripPrefix() string {
return c.stripPrefix
}

// LogLevel returns the log level.
func (c *cfg) LogLevel() string {
return c.logLevel
Expand Down
8 changes: 8 additions & 0 deletions internal/configs/logarea/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
type Config interface {
ServiceHost() string
ServicePort() string
StripPrefix() string
LogLevel() string
LogFilePath() string
ETOSNamespace() string
Expand All @@ -35,6 +36,7 @@ type Config interface {
type cfg struct {
serviceHost string
servicePort string
stripPrefix string
logLevel string
logFilePath string
etosNamespace string
Expand All @@ -48,6 +50,7 @@ func Get() Config {

flag.StringVar(&conf.serviceHost, "address", EnvOrDefault("SERVICE_HOST", "127.0.0.1"), "Address to serve API on")
flag.StringVar(&conf.servicePort, "port", EnvOrDefault("SERVICE_PORT", "8080"), "Port to serve API on")
flag.StringVar(&conf.stripPrefix, "stripprefix", EnvOrDefault("STRIP_PREFIX", ""), "Strip a URL prefix. Useful when a reverse proxy sets a subpath. I.e. reverse proxy sets /stream as prefix, making the etos API available at /stream/v1/events. In that case we want to set stripprefix to /stream")
flag.StringVar(&conf.logLevel, "loglevel", EnvOrDefault("LOGLEVEL", "INFO"), "Log level (TRACE, DEBUG, INFO, WARNING, ERROR, FATAL, PANIC).")
flag.StringVar(&conf.logFilePath, "logfilepath", os.Getenv("LOG_FILE_PATH"), "Path, including filename, for the log files to create.")
flag.StringVar(&conf.etosNamespace, "etosnamespace", ReadNamespaceOrEnv("ETOS_NAMESPACE"), "Path, including filename, for the log files to create.")
Expand All @@ -68,6 +71,11 @@ func (c *cfg) ServicePort() string {
return c.servicePort
}

// StripPrefix returns the prefix to strip. Empty string if no prefix.
func (c *cfg) StripPrefix() string {
return c.stripPrefix
}

// LogLevel returns the log level.
func (c *cfg) LogLevel() string {
return c.logLevel
Expand Down
8 changes: 8 additions & 0 deletions internal/configs/sse/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
type Config interface {
ServiceHost() string
ServicePort() string
StripPrefix() string
LogLevel() string
LogFilePath() string
ETOSNamespace() string
Expand All @@ -35,6 +36,7 @@ type Config interface {
type cfg struct {
serviceHost string
servicePort string
stripPrefix string
logLevel string
logFilePath string
etosNamespace string
Expand All @@ -48,6 +50,7 @@ func Get() Config {

flag.StringVar(&conf.serviceHost, "address", EnvOrDefault("SERVICE_HOST", "127.0.0.1"), "Address to serve API on")
flag.StringVar(&conf.servicePort, "port", EnvOrDefault("SERVICE_PORT", "8080"), "Port to serve API on")
flag.StringVar(&conf.stripPrefix, "stripprefix", EnvOrDefault("STRIP_PREFIX", ""), "Strip a URL prefix. Useful when a reverse proxy sets a subpath. I.e. reverse proxy sets /stream as prefix, making the etos API available at /stream/v1/events. In that case we want to set stripprefix to /stream")
flag.StringVar(&conf.logLevel, "loglevel", EnvOrDefault("LOGLEVEL", "INFO"), "Log level (TRACE, DEBUG, INFO, WARNING, ERROR, FATAL, PANIC).")
flag.StringVar(&conf.logFilePath, "logfilepath", os.Getenv("LOG_FILE_PATH"), "Path, including filename, for the log files to create.")
flag.StringVar(&conf.etosNamespace, "etosnamespace", ReadNamespaceOrEnv("ETOS_NAMESPACE"), "Path, including filename, for the log files to create.")
Expand All @@ -68,6 +71,11 @@ func (c *cfg) ServicePort() string {
return c.servicePort
}

// StripPrefix returns the prefix to strip. Empty string if no prefix.
func (c *cfg) StripPrefix() string {
return c.stripPrefix
}

// LogLevel returns the log level.
func (c *cfg) LogLevel() string {
return c.logLevel
Expand Down
5 changes: 4 additions & 1 deletion internal/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ type WebService struct {

// NewWebService creates a new Server of the webservice type.
func NewWebService(cfg config.Config, log *logrus.Entry, handler http.Handler) Server {
if cfg.StripPrefix() != "" {
handler = http.StripPrefix(cfg.StripPrefix(), handler)
}
webservice := &WebService{
server: &http.Server{
Addr: fmt.Sprintf("%s:%s", cfg.ServiceHost(), cfg.ServicePort()),
Expand All @@ -52,7 +55,7 @@ func NewWebService(cfg config.Config, log *logrus.Entry, handler http.Handler) S

// Start a webservice and block until closed or crashed.
func (s *WebService) Start() error {
s.logger.Infof("Starting webservice listening on %s:%s", s.cfg.ServiceHost(), s.cfg.ServicePort())
s.logger.Infof("Starting webservice listening on %s:%s%s", s.cfg.ServiceHost(), s.cfg.ServicePort(), s.cfg.StripPrefix())
return s.server.ListenAndServe()
}

Expand Down
10 changes: 10 additions & 0 deletions manifests/base/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ rules:
- delete
- list
- watch
- apiGroups:
- "etos.eiffel-community.github.io"
resources:
- testruns
verbs:
- create
- get
- delete
- list
- watch
- apiGroups:
- ""
resources:
Expand Down
4 changes: 3 additions & 1 deletion pkg/application/application.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
// limitations under the License.
package application

import "github.com/julienschmidt/httprouter"
import (
"github.com/julienschmidt/httprouter"
)

type Application interface {
LoadRoutes(*httprouter.Router)
Expand Down
4 changes: 2 additions & 2 deletions python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ classifiers = [
]
requires-python = ">=3.9"
dependencies = [
"etos_lib==4.3.1",
"etos_lib==4.4.1",
"etcd3gw~=2.3",
"uvicorn~=0.22",
"fastapi~=0.109.1",
Expand Down Expand Up @@ -62,4 +62,4 @@ testpaths = ["tests"]
root = ".."

[tool.setuptools.packages]
find = { where = ["src"], exclude = ["tests"] }
find = { where = ["src"], exclude = ["tests"] }
29 changes: 4 additions & 25 deletions python/src/etos_api/library/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
from typing import List, Union
from uuid import UUID

import requests

# Pylint refrains from linting C extensions due to arbitrary code execution.
from pydantic import BaseModel # pylint:disable=no-name-in-module
from pydantic import ValidationError, conlist, constr, field_validator
Expand Down Expand Up @@ -157,33 +155,14 @@ class SuiteValidator:

logger = logging.getLogger(__name__)

async def _download_suite(self, test_suite_url):
"""Attempt to download suite.

:param test_suite_url: URL to test suite to download.
:type test_suite_url: str
:return: Downloaded test suite as JSON.
:rtype: list
"""
try:
suite = requests.get(test_suite_url, timeout=60)
suite.raise_for_status()
except Exception as exception: # pylint:disable=broad-except
raise AssertionError(f"Unable to download suite from {test_suite_url}") from exception
return suite.json()

async def validate(self, test_suite_url):
async def validate(self, test_suite):
"""Validate the ETOS suite definition.

:param test_suite_url: URL to test suite that is being executed.
:type test_suite_url: str
:param test_suite: Test suite that is being executed.
:type test_suite: list
:raises ValidationError: If the suite did not validate.
"""
downloaded_suite = await self._download_suite(test_suite_url)
assert (
len(downloaded_suite) > 0
), "Suite definition validation unsuccessful - Reason: Empty Test suite definition list"
for suite_json in downloaded_suite:
for suite_json in test_suite:
test_runners = set()
suite = Suite(**suite_json)
assert suite
Expand Down
5 changes: 3 additions & 2 deletions python/src/etos_api/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@

from fastapi import FastAPI

# from opentelemetry.sdk.trace import TracerProvider
from starlette.responses import RedirectResponse

from etos_api import routers

APP = FastAPI()
# This allows the path to start either at '/api' or '/'.
APP = FastAPI(root_path="/api")
LOGGER = logging.getLogger(__name__)


Expand Down Expand Up @@ -52,5 +52,6 @@ async def redirect_head_to_root():


APP.include_router(routers.etos.ROUTER)
APP.include_router(routers.testrun.ROUTER)
APP.include_router(routers.selftest.ROUTER)
APP.include_router(routers.logs.ROUTER)
10 changes: 9 additions & 1 deletion python/src/etos_api/routers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
"""ETOS API routers module."""
from . import etos, logs, selftest
import os
from kubernetes import config
from . import etos, testrun, logs, selftest

if os.getenv("RUNNING_TESTS") is None:
try:
config.load_incluster_config()
except config.ConfigException:
config.load_config()
18 changes: 17 additions & 1 deletion python/src/etos_api/routers/etos/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from kubernetes import client
from opentelemetry import trace
from opentelemetry.trace import Span
import requests

from etos_api.library.environment import Configuration, configure_testrun
from etos_api.library.utilities import sync_to_async
Expand All @@ -39,6 +40,20 @@
logging.getLogger("pika").setLevel(logging.WARNING)


async def download_suite(test_suite_url: str) -> dict:
"""Attempt to download suite.

:param test_suite_url: URL to test suite to download.
:return: Downloaded test suite as JSON.
"""
try:
suite = requests.get(test_suite_url, timeout=60)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we perhaps add retries? Doesn't etos-library have convenience utils for this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding retries is a bit harder than it seems due to the fact that we are using asyncio. We would need to change from requests to another library for downloads and make sure we have retries that work for that library and the verification would take longer.

We've not had any retries for this function before (I just moved this from the validator to the router), so I'm not making any changes to it and would rather we write a change request on ETOS to fix this.

suite.raise_for_status()
except Exception as exception: # pylint:disable=broad-except
raise AssertionError(f"Unable to download suite from {test_suite_url}") from exception
return suite.json()


async def validate_suite(test_suite_url: str) -> None:
"""Validate the ETOS test suite through the SuiteValidator.

Expand All @@ -47,7 +62,8 @@ async def validate_suite(test_suite_url: str) -> None:
span = trace.get_current_span()

try:
await SuiteValidator().validate(test_suite_url)
test_suite = await download_suite(test_suite_url)
await SuiteValidator().validate(test_suite)
except AssertionError as exception:
LOGGER.error("Test suite validation failed!")
LOGGER.error(exception)
Expand Down
10 changes: 0 additions & 10 deletions python/src/etos_api/routers/lib/kubernetes.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,9 @@
import logging
import os

from kubernetes import config

NAMESPACE_FILE = "/var/run/secrets/kubernetes.io/serviceaccount/namespace"
LOGGER = logging.getLogger(__name__)

try:
config.load_incluster_config()
except config.ConfigException:
try:
config.load_config()
except config.ConfigException:
LOGGER.warning("Could not load a Kubernetes config")


def namespace() -> str:
"""Get current namespace if available."""
Expand Down
18 changes: 18 additions & 0 deletions python/src/etos_api/routers/testrun/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright Axis Communications AB.
#
# For a full list of individual contributors, please see the commit history.
#
# Licensed 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.
"""ETOS API testrun module."""
from .router import ROUTER
from . import schemas
Loading
Loading