Skip to content

Commit

Permalink
[CE-624] Add node update api for agent launcher
Browse files Browse the repository at this point in the history
Add NODE_UPDATE_URL for agent launcher.
Add API_VERSION for swagger document shown.
Can update node info through node update api.

Change-Id: I42a406d6805a2707502a0b207d25294f86f76a28
Signed-off-by: Haitao Yue <[email protected]>
  • Loading branch information
hightall committed Jun 18, 2019
1 parent f17b841 commit 57f12ab
Show file tree
Hide file tree
Showing 9 changed files with 155 additions and 13 deletions.
1 change: 1 addition & 0 deletions .makerc/api-engine
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export API_ENGINE_ADMIN_USERNAME?=admin
export API_ENGINE_ADMIN_PASSWORD?=pass
export [email protected]
export API_DOCKER_HOST?=unix://var/run/docker.sock
export API_VERSION?=master

ifeq ($(DEPLOY_METHOD), docker-compose)
export API_ENGINE_SSO_AUTH_URL?=http://${SERVER_PUBLIC_IP}:${KEYCLOAK_SERVER_PORT}/auth/
Expand Down
1 change: 1 addition & 0 deletions bootup/kubernetes/templates/api-engine/config.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ data:
ADMIN_PASSWORD: $API_ENGINE_ADMIN_PASSWORD
ADMIN_EMAIL: $API_ENGINE_ADMIN_EMAIL
DOCKER_HOST: "$API_DOCKER_HOST"
API_VERSION: "$API_VERSION"
41 changes: 39 additions & 2 deletions src/agent-launcher/fabric/kubernetes-agent/src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,29 @@
#
import os
import zipfile
import requests
import json
from utils import download_file, KubernetesClient
from network import FabricNetwork
from enum import Enum, unique


@unique
class AgentOperation(Enum):
Start = "start"
Query = "query"
Update = "update"
Delete = "delete"


@unique
class NodeStatus(Enum):
Deploying = "deploying"
Running = "running"
Stopped = "stopped"
Deleting = "deleting"
Error = "error"


AGENT_URL = os.getenv("AGENT_URL")
DEPLOY_NAME = os.getenv("DEPLOY_NAME")
Expand All @@ -15,6 +36,8 @@
AGENT_ID = os.getenv("AGENT_ID")
NODE_ID = os.getenv("NODE_ID")
OPERATION = os.getenv("OPERATION")
TOKEN = os.getenv("TOKEN")
NODE_UPDATE_URL = os.getenv("NODE_UPDATE_URL")

if __name__ == "__main__":
config_file = download_file(AGENT_CONFIG_FILE, "/tmp")
Expand All @@ -40,7 +63,14 @@
service = config.get("service")
ingress = config.get("ingress")

if OPERATION == "start":
# authorization headers for call update api
headers = {
"Authorization": "JWT %s" % TOKEN,
"Content-Type": "application/json",
}
ports = []

if OPERATION == AgentOperation.Start.value:
if deployment:
k8s_client.create_deployment(AGENT_ID, **deployment)
if service:
Expand All @@ -53,6 +83,13 @@
{"external": port.node_port, "internal": port.port}
for port in ports
]
# set_ports_mapping(self._node_id, ports, True)
if ingress:
k8s_client.create_ingress(AGENT_ID, **ingress)

ret = requests.put(
url=NODE_UPDATE_URL,
headers=headers,
data=json.dumps(
{"status": NodeStatus.Running.value, "ports": ports}
),
)
23 changes: 22 additions & 1 deletion src/api-engine/api/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,35 @@
import os

from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
from rest_framework import authentication
from rest_framework.permissions import BasePermission

from api.common.enums import UserRole
from api.models import UserProfile

LOG = logging.getLogger(__name__)
TOKEN_INFO_URL = getattr(settings, "TOKEN_INFO_URL", "")
SUPER_USER_TOKEN = os.environ.get("ADMIN_TOKEN", "")
ADMIN_NAME = getattr(settings, "ADMIN_NAME", "Administrator")
ADMIN_NAME = os.getenv("ADMIN_USERNAME")


class CustomAuthenticate(authentication.BaseAuthentication):
def authenticate(self, request):
authorization = request.META.get("HTTP_AUTHORIZATION", None)
if not authorization or not authorization.startswith("JWT"):
return None
token = authorization.split(" ")[-1]
if token == SUPER_USER_TOKEN:
username = ADMIN_NAME
try:
user = UserProfile.objects.get(username=username)
except ObjectDoesNotExist:
return None

return user, None
else:
return None


class IsAdminAuthenticated(BasePermission):
Expand Down
14 changes: 12 additions & 2 deletions src/api-engine/api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
LIMIT_K8S_CONFIG_FILE_MB = 100
# Limit file upload size less than 100Mb
LIMIT_FILE_MB = 100
MIN_PORT = 1
MAX_PORT = 65535


class Govern(models.Model):
Expand Down Expand Up @@ -421,8 +423,16 @@ class Port(models.Model):
node = models.ForeignKey(
Node, help_text="Node of port", on_delete=models.CASCADE, null=True
)
external = models.IntegerField(help_text="External port", default=0)
internal = models.IntegerField(help_text="Internal port", default=0)
external = models.IntegerField(
help_text="External port",
default=0,
validators=[MinValueValidator(MIN_PORT), MaxValueValidator(MAX_PORT)],
)
internal = models.IntegerField(
help_text="Internal port",
default=0,
validators=[MinValueValidator(MIN_PORT), MaxValueValidator(MAX_PORT)],
)

class Meta:
ordering = ("external",)
Expand Down
20 changes: 19 additions & 1 deletion src/api-engine/api/routes/node/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
HostType,
)
from api.common.serializers import PageQuerySerializer
from api.models import Node
from api.models import Node, Port

LOG = logging.getLogger(__name__)

Expand Down Expand Up @@ -124,6 +124,24 @@ def validate(self, attrs):
return attrs


class PortSerializer(serializers.ModelSerializer):
class Meta:
model = Port
fields = ("external", "internal")
extra_kwargs = {
"external": {"required": True},
"internal": {"required": True},
}


class NodeUpdateBody(serializers.ModelSerializer):
ports = PortSerializer(help_text="Port mapping for node", many=True)

class Meta:
model = Node
fields = ("status", "ports")


class NodeOperationSerializer(serializers.Serializer):
action = serializers.ChoiceField(
help_text=Operation.get_info("Operation for node:", list_str=True),
Expand Down
59 changes: 54 additions & 5 deletions src/api-engine/api/routes/node/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,45 @@
from django.core.exceptions import PermissionDenied
from django.core.paginator import Paginator
from django.db.models import Count, F
from django.urls import reverse
from drf_yasg.utils import swagger_auto_schema
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework_jwt.authentication import JSONWebTokenAuthentication

from api.auth import IsOperatorAuthenticated
from api.common.enums import NodeStatus
from api.exceptions import CustomError, NoResource
from api.exceptions import ResourceNotFound
from api.models import Agent, Node
from api.models import Agent, Node, Port
from api.routes.node.serializers import (
NodeOperationSerializer,
NodeQuery,
NodeCreateBody,
NodeIDSerializer,
NodeListSerializer,
NodeUpdateBody,
)
from api.tasks import create_node, delete_node
from api.utils.common import with_common_response
from api.auth import CustomAuthenticate

LOG = logging.getLogger(__name__)


class NodeViewSet(viewsets.ViewSet):
authentication_classes = (JSONWebTokenAuthentication,)
permission_classes = (IsAuthenticated,)
authentication_classes = (CustomAuthenticate, JSONWebTokenAuthentication)

# Only operator can update node info
def get_permissions(self):
if self.action in ["update"]:
permission_classes = (IsAuthenticated, IsOperatorAuthenticated)
else:
permission_classes = (IsAuthenticated,)

return [permission() for permission in permission_classes]

@staticmethod
def _validate_organization(request):
Expand Down Expand Up @@ -150,11 +162,15 @@ def create(self, request):
agent_config_file = (
request.build_absolute_uri(agent.config_file.url),
)
node_update_api = reverse("node-detail", args=[str(node.id)])
node_update_api = request.build_absolute_uri(node_update_api)
if isinstance(agent_config_file, tuple):
agent_config_file = list(agent_config_file)[0]
# TODO: add node update api value
create_node.delay(
str(node.id), agent.image, agent_config_file=agent_config_file
str(node.id),
agent.image,
agent_config_file=agent_config_file,
node_update_api=node_update_api,
)
response = NodeIDSerializer(data={"id": str(node.id)})
if response.is_valid(raise_exception=True):
Expand Down Expand Up @@ -207,3 +223,36 @@ def destroy(self, request, pk=None):
node.delete()

return Response(status=status.HTTP_204_NO_CONTENT)

@swagger_auto_schema(
operation_id="update node",
request_body=NodeUpdateBody,
responses=with_common_response({status.HTTP_202_ACCEPTED: "Accepted"}),
)
def update(self, request, pk=None):
"""
Update Node
Update special node with id.
"""
serializer = NodeUpdateBody(data=request.data)
if serializer.is_valid(raise_exception=True):
node_status = serializer.validated_data.get("status")
ports = serializer.validated_data.get("ports")
try:
node = Node.objects.get(id=pk)
except ObjectDoesNotExist:
raise ResourceNotFound

node.status = node_status
node.save()

for port_item in ports:
port = Port(
external=port_item.get("external"),
internal=port_item.get("internal"),
node=node,
)
port.save()

return Response(status=status.HTTP_202_ACCEPTED)
6 changes: 5 additions & 1 deletion src/api-engine/api/tasks/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from __future__ import absolute_import, unicode_literals

import logging
import os

import docker
from django.core.exceptions import ObjectDoesNotExist
Expand All @@ -13,6 +14,7 @@
from api_engine.celery import app

LOG = logging.getLogger(__name__)
ADMIN_TOKEN = os.getenv("ADMIN_TOKEN")


@app.task(bind=True, default_retry_delay=5, max_retries=3, time_limit=360)
Expand All @@ -34,7 +36,9 @@ def create_node(self, node_id=None, agent_image=None, **kwargs):
"AGENT_ID": str(node.agent.id),
"AGENT_CONFIG_FILE": agent_config_file,
"NODE_UPDATE_URL": node_update_api,
"OPERATION": AgentOperation.Start,
# Token for call update node api
"TOKEN": ADMIN_TOKEN,
"OPERATION": AgentOperation.Start.value,
}
client = docker.from_env()
client.containers.run(
Expand Down
3 changes: 2 additions & 1 deletion src/api-engine/api_engine/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,14 @@
from api.routes.file.views import FileViewSet

DEBUG = getattr(settings, "DEBUG")
API_VERSION = os.getenv("API_VERSION")
WEBROOT = os.getenv("WEBROOT")
WEBROOT = "/".join(WEBROOT.split("/")[1:]) + "/"


swagger_info = openapi.Info(
title="Cello API Engine Service",
default_version="v1",
default_version=API_VERSION,
description="""
This is swagger docs for Cello API engine.
""",
Expand Down

0 comments on commit 57f12ab

Please sign in to comment.