├── mk ├── Version ├── Erlangbin.mk ├── Erlanglib.mk └── Docker.mk ├── include └── kube_vxlan_controller.hrl ├── rebar3 ├── .gitignore ├── docker ├── docker-entrypoint.sh └── Dockerfile ├── Makefile ├── priv ├── account.sh ├── account.yaml ├── kube-vxlan-controller.config ├── test.yaml └── admin_bin ├── config ├── sys.config └── vm.args ├── src ├── kube-vxlan-controller.erl ├── kube_vxlan_controller_pod_sup.erl ├── kube-vxlan-controller.app.src.script ├── kube_vxlan_controller_list.erl ├── kube_vxlan_controller_format.erl ├── kube_vxlan_controller_sup.erl ├── kube_vxlan_controller_db.erl ├── kube_vxlan_controller_state.erl ├── kube_vxlan_controller_agent.erl ├── kube_vxlan_controller_inspect.erl ├── kube_vxlan_controller_config.erl ├── kube_vxlan_controller_pod_reg.erl ├── kube_vxlan_controller_tools.erl ├── kube_vxlan_controller_cli.erl ├── logger_sys_debug.erl ├── kube_vxlan_controller_net.erl ├── kube_vxlan_controller_k8s.erl ├── kube_vxlan_controller_run.erl └── kube_vxlan_controller_pod.erl ├── rebar.config ├── .travis.yml ├── kubernetes ├── bundle.yaml └── example.yaml ├── LICENSE.md └── README.md /mk/Version: -------------------------------------------------------------------------------- 1 | 1.0.0 (git-48184c1) 2 | -------------------------------------------------------------------------------- /include/kube_vxlan_controller.hrl: -------------------------------------------------------------------------------- 1 | -record(uid, {id, cycle}). 2 | -------------------------------------------------------------------------------- /rebar3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openvnf/kube-vxlan-controller/HEAD/rebar3 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .eunit 2 | deps 3 | *.o 4 | *.beam 5 | *.plt 6 | erl_crash.dump 7 | ebin 8 | .rebar 9 | *~ 10 | _build 11 | -------------------------------------------------------------------------------- /docker/docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # copy of configuration files 3 | RELPATH=$(find /opt/kube-vxlan-controller/releases/ -mindepth 1 -maxdepth 1 -type d) 4 | [ -f /config/kube-vxlan-controller/sys.config ] && cp /config/kube-vxlan-controller/sys.config $RELPATH/sys.config 5 | [ -f /config/kube-vxlan-controller/vm.args ] && cp /config/kube-vxlan-controller/vm.args $RELPATH/vm.args 6 | 7 | exec "$@" 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include mk/Erlangbin.mk 2 | include mk/Docker.mk 3 | 4 | USER = openvnf 5 | 6 | RUN_ARGS = run 7 | 8 | DOCKER_RUN_ARGS_EXTRA = \ 9 | -v ${PWD}/priv/$(PROJECT).config:/etc/$(PROJECT)/config \ 10 | -v ${PWD}/pki:/var/run/$(PROJECT)/pki \ 11 | -w /var/run/$(PROJECT) 12 | 13 | ifdef ERLANG_VERSION 14 | DOCKER_BUILD_ARGS_EXTRA = \ 15 | --build-arg ERLANG_VERSION=$(ERLANG_VERSION) 16 | endif 17 | -------------------------------------------------------------------------------- /priv/account.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | kubectl apply -f priv/account.yaml 3 | 4 | kubectl get -n kube-system secrets/kube-vxlan-controller-token-xbtcs -o=jsonpath='{.data.namespace}' | cut -d " " -f4 | base64 -d > pki/namespace 5 | kubectl get -n kube-system secrets/kube-vxlan-controller-token-xbtcs -o=jsonpath='{.data.ca\.crt}' | cut -d " " -f4 | base64 -d > pki/ca.crt 6 | kubectl get -n kube-system secrets/kube-vxlan-controller-token-xbtcs -o=jsonpath='{.data.token}' | cut -d " " -f4 | base64 -d > pki/token 7 | -------------------------------------------------------------------------------- /config/sys.config: -------------------------------------------------------------------------------- 1 | %-*-Erlang-*- 2 | [{kernel, 3 | [{logger_level, debug}, 4 | {logger, 5 | [{handler, default, logger_std_h, 6 | #{level => debug, 7 | formatter => 8 | {logger_formatter, 9 | #{single_line => false, 10 | legacy_header => false, 11 | template => [time," ",pid," ",level,": ",msg,"\n"] 12 | }}, 13 | config => 14 | #{sync_mode_qlen => 10000, 15 | drop_mode_qlen => 10000, 16 | flush_qlen => 10000} 17 | } 18 | } 19 | ]} 20 | ]} 21 | ]. 22 | -------------------------------------------------------------------------------- /priv/account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: kube-vxlan-controller 5 | namespace: kube-system 6 | --- 7 | apiVersion: rbac.authorization.k8s.io/v1beta1 8 | kind: ClusterRoleBinding 9 | metadata: 10 | name: kube-vxlan-controller 11 | roleRef: 12 | apiGroup: rbac.authorization.k8s.io 13 | kind: ClusterRole 14 | name: cluster-admin 15 | subjects: 16 | - kind: ServiceAccount 17 | name: kube-vxlan-controller 18 | namespace: kube-system 19 | --- 20 | apiVersion: v1 21 | kind: ConfigMap 22 | metadata: 23 | name: kube-vxlan-controller 24 | namespace: kube-system 25 | data: 26 | vxeth0: id=100 up 27 | vxeth1: id=101 up 28 | vxeth2: id=102 up 29 | vxeth3: id=103 up -------------------------------------------------------------------------------- /src/kube-vxlan-controller.erl: -------------------------------------------------------------------------------- 1 | -module('kube-vxlan-controller'). 2 | 3 | -behaviour(application). 4 | 5 | -define(AppSup, kube_vxlan_controller_sup). 6 | -define(Config, kube_vxlan_controller_config). 7 | 8 | %% Application callbacks 9 | -export([start/2, stop/1]). 10 | 11 | %%%=================================================================== 12 | %%% Application callbacks 13 | %%%=================================================================== 14 | 15 | start(_StartType, _StartArgs) -> 16 | case ?Config:load(#{}) of 17 | {ok, Config} -> 18 | ?AppSup:start_link(Config); 19 | {error, Reason} = Error -> 20 | io:format("~p~n", [Reason]), 21 | Error 22 | end. 23 | 24 | stop(_State) -> 25 | ok. 26 | -------------------------------------------------------------------------------- /priv/kube-vxlan-controller.config: -------------------------------------------------------------------------------- 1 | %-*-Erlang-*- 2 | [{kernel, 3 | [{logger_level, debug}, 4 | {logger, 5 | [{handler, default, logger_std_h, 6 | #{level => debug, 7 | formatter => 8 | {logger_formatter, 9 | #{single_line => false, 10 | legacy_header => false, 11 | template => [time," ",pid," ",level,": ",msg,"\n"] 12 | }}, 13 | config => 14 | #{sync_mode_qlen => 10000, 15 | drop_mode_qlen => 10000, 16 | flush_qlen => 10000} 17 | } 18 | } 19 | ]} 20 | ]}, 21 | 22 | {'kube-vxlan-controller', 23 | [{server, "https://127.0.0.1:6443"}, 24 | {ca_cert_file, "pki/ca.crt"}, 25 | {namespace_file, "pki/namespace"}, 26 | {token_file, "pki/token"}, 27 | {selector, "vxlan.travelping.com"}, 28 | {annotation, "vxlan.travelping.com/networks"} 29 | ]} 30 | ]. 31 | -------------------------------------------------------------------------------- /src/kube_vxlan_controller_pod_sup.erl: -------------------------------------------------------------------------------- 1 | -module(kube_vxlan_controller_pod_sup). 2 | 3 | -behaviour(supervisor). 4 | 5 | %% API 6 | -export([start_link/0, start_pod/3]). 7 | 8 | %% Supervisor callbacks 9 | -export([init/1]). 10 | 11 | -define(SERVER, ?MODULE). 12 | -define(Pod, kube_vxlan_controller_pod). 13 | 14 | %% =================================================================== 15 | %% API functions 16 | %% =================================================================== 17 | 18 | start_link() -> 19 | supervisor:start_link({local, ?SERVER}, ?MODULE, []). 20 | 21 | start_pod(Id, Event, Config) -> 22 | supervisor:start_child(?SERVER, [Id, Event, Config]). 23 | 24 | %% =================================================================== 25 | %% Supervisor callbacks 26 | %% =================================================================== 27 | 28 | init([]) -> 29 | {ok, {{simple_one_for_one, 5, 10}, 30 | [{?Pod, {?Pod, start_link, []}, temporary, 1000, worker, [?Pod]}]}}. 31 | -------------------------------------------------------------------------------- /rebar.config: -------------------------------------------------------------------------------- 1 | %-*-Erlang-*- 2 | {deps, [{cpflib, {git, "https://github.com/aialferov/cpflib.git", {tag, "master"}}}, 3 | {regine, {git, "https://github.com/travelping/regine.git", {branch, "master"}}}, 4 | {jsx, {git, "https://github.com/talentdeficit/jsx.git", {tag, "v3.0.0"}}}, 5 | {gun, {git, "https://github.com/ninenines/gun.git", {tag, "2.0.0-pre.2"}}} 6 | ]}. 7 | 8 | {relx, [{release, {'kube-vxlan-controller', "1.0.0"}, 9 | ['kube-vxlan-controller', inets, cpflib, jsx, gun, regine, sasl]}, 10 | 11 | {sys_config, "./config/sys.config"}, 12 | {vm_args, "./config/vm.args"}, 13 | 14 | {mode, prod}, 15 | {overlay, [ 16 | {mkdir, "bin"}, 17 | {template, "./priv/admin_bin", "bin/vxlan-admin"} 18 | ]} 19 | ]}. 20 | 21 | {shell, [{apps, ['kube-vxlan-controller']}, 22 | {config, "priv/kube-vxlan-controller.config"}]}. 23 | 24 | %% xref checks to run 25 | {xref_checks, [locals_not_used, deprecated_function_calls, 26 | deprecated_functions]}. 27 | {xref_queries, 28 | [{"(XC - UC) || (XU - X - B)", []}]}. 29 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | # -- build-environment -- 2 | # see https://docs.docker.com/engine/userguide/eng-image/multistage-build/ 3 | 4 | FROM erlang:23.0.4-alpine AS build-env 5 | LABEL project=kube-vxlan-controller 6 | 7 | WORKDIR /build 8 | RUN apk update && apk --no-cache upgrade && \ 9 | apk --no-cache add \ 10 | gcc \ 11 | git \ 12 | libc-dev libc-utils \ 13 | libgcc \ 14 | linux-headers \ 15 | make bash \ 16 | musl-dev musl-utils \ 17 | ncurses-dev \ 18 | pcre2 \ 19 | pkgconf \ 20 | scanelf \ 21 | zlib 22 | 23 | ADD . /build 24 | RUN rebar3 as prod release 25 | 26 | # -- runtime image -- 27 | 28 | FROM alpine:3.11 29 | LABEL project=kube-vxlan-controller 30 | 31 | WORKDIR / 32 | RUN apk update && \ 33 | apk --no-cache upgrade && \ 34 | apk --no-cache add zlib ncurses-libs libcrypto1.1 tini 35 | 36 | COPY docker/docker-entrypoint.sh / 37 | COPY --from=build-env /build/_build/prod/rel/ /opt/ 38 | 39 | ENTRYPOINT ["tini", "--", "/docker-entrypoint.sh"] 40 | CMD ["/opt/kube-vxlan-controller/bin/kube-vxlan-controller", "foreground"] 41 | -------------------------------------------------------------------------------- /config/vm.args: -------------------------------------------------------------------------------- 1 | ## Name of the node 2 | -sname kube-vxlan-controller 3 | 4 | ## Cookie for distributed erlang 5 | -setcookie kube-vxlan-controller 6 | 7 | ## Heartbeat management; auto-restarts VM if it dies or becomes unresponsive 8 | ## (Disabled by default..use with caution!) 9 | ##-heart 10 | 11 | ## Enable kernel poll and a few async threads 12 | ##+K true 13 | ##+A 5 14 | 15 | ## Increase number of concurrent ports/sockets 16 | ##-env ERL_MAX_PORTS 4096 17 | 18 | ## Tweak GC to run more often 19 | ##-env ERL_FULLSWEEP_AFTER 10 20 | 21 | # +B [c | d | i] 22 | # Option c makes Ctrl-C interrupt the current shell instead of invoking the emulator break 23 | # handler. Option d (same as specifying +B without an extra option) disables the break handler. # Option i makes the emulator ignore any break signal. 24 | # If option c is used with oldshell on Unix, Ctrl-C will restart the shell process rather than 25 | # interrupt it. 26 | # Disable the emulator break handler 27 | # it easy to accidentally type ctrl-c when trying 28 | # to reach for ctrl-d. ctrl-c on a live node can 29 | # have very undesirable results 30 | ##+Bi 31 | -------------------------------------------------------------------------------- /src/kube-vxlan-controller.app.src.script: -------------------------------------------------------------------------------- 1 | {application, 'kube-vxlan-controller', [ 2 | {description, "Kubernetes VXLAN contoller"}, 3 | {vsn, "1.0.0"}, 4 | {modules, []}, 5 | {applications, [kernel, stdlib, inets, cpflib, jsx, regine, gun]}, 6 | {mod, {'kube-vxlan-controller', []}}, 7 | {env, [ 8 | {git_sha, lists:sublist(os:cmd("git rev-parse HEAD 2>/dev/null"), 8)}, 9 | {config_files, [ 10 | "priv/kube-vxlan-controller.config", 11 | "/etc/kube-vxlan-controller/config" 12 | ]}, 13 | {db_file, "db/kube-vxlan-controller.db"}, 14 | {server, "https://kubernetes.default"}, 15 | {namespace_file, "/var/run/secrets/kubernetes.io/serviceaccount/namespace"}, 16 | {ca_cert_file, "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"}, 17 | {token_file, "/var/run/secrets/kubernetes.io/serviceaccount/token"}, 18 | {selector, "vxlan.default"}, 19 | {annotation, "vxlan.default/networks"}, 20 | {configmap_name, "kube-vxlan-controller"}, 21 | {agent_container_name, "vxlan-controller-agent"}, 22 | {agent_init_container_name, "vxlan-controller-agent-init"}, 23 | {fields, "pod,net,fdb"} 24 | ]} 25 | ]}. 26 | -------------------------------------------------------------------------------- /src/kube_vxlan_controller_list.erl: -------------------------------------------------------------------------------- 1 | -module(kube_vxlan_controller_list). 2 | 3 | -export([ 4 | pods/1, pods/2 5 | ]). 6 | 7 | -define(Db, kube_vxlan_controller_db). 8 | -define(Net, kube_vxlan_controller_net). 9 | -define(Pod, kube_vxlan_controller_pod). 10 | -define(Tools, kube_vxlan_controller_tools). 11 | 12 | pods(Config) -> pods([], Config). 13 | 14 | pods(PodNamePrefixes, Config) -> 15 | SilentConfig = maps:put(silent, true, Config), 16 | 17 | {ok, PodResources} = ?Pod:get({label, maps:get(selector, Config)}, Config), 18 | Pods = ?Tools:pods(PodResources, ?Db:nets_options(), [], Config), 19 | 20 | [io:format("~s/~s ~s ~s~n", [Namespace, PodName, NetName, Ip]) || 21 | Pod = #{namespace := Namespace, name := PodName} <- Pods, 22 | PodNamePrefixes == [] orelse lists:any( 23 | fun(Prefix) -> lists:prefix(Prefix, PodName) end, 24 | PodNamePrefixes 25 | ), 26 | {NetName, _NetOptions} <- maps:get(nets, Pod), 27 | Ip <- ips(Pod, NetName, SilentConfig)]. 28 | 29 | ips(Pod, NetName, Config) -> 30 | case ?Net:ips(Pod, NetName, Config) of 31 | [] -> [""]; 32 | Ips -> Ips 33 | end. 34 | -------------------------------------------------------------------------------- /src/kube_vxlan_controller_format.erl: -------------------------------------------------------------------------------- 1 | -module(kube_vxlan_controller_format). 2 | 3 | -export([ 4 | pod/1, 5 | pod_nets/2, 6 | pod_net/1, 7 | pod_net_options/1, 8 | 9 | fdb/2, 10 | dev/2, 11 | route/2 12 | ]). 13 | 14 | pod(#{namespace := Namespace, name := Name, ip := Ip}) -> 15 | format("~s/~s ~s", [Namespace, Name, Ip]). 16 | 17 | pod_nets(Nets, Ident) -> 18 | format_list([pod_net(Net) || Net <- Nets], Ident). 19 | 20 | pod_net({Name, Options}) -> 21 | format("~s: ~s", [Name, pod_net_options(Options)]). 22 | 23 | pod_net_options(Options) -> 24 | string:trim(maps:fold(fun(Name, Value, Acc) -> 25 | Acc ++ format("~s:~s ", [Name, Value]) 26 | end, "", Options)). 27 | 28 | dev(Dev, Ident) -> 29 | format_list([L || [_,_,_|L] <- string:lexemes(Dev, "\n")], Ident). 30 | 31 | fdb(Fdb, Ident) -> format_list(string:lexemes(Fdb, "\n"), Ident). 32 | route(Route, Ident) -> format_list(string:lexemes(Route, "\n"), Ident). 33 | 34 | format_list(List, Ident) -> 35 | IdentString = lists:duplicate(Ident, $ ), 36 | IdentString ++ lists:join([$\n|IdentString], List). 37 | 38 | format(Format, Args) -> lists:flatten(io_lib:format(Format, Args)). 39 | -------------------------------------------------------------------------------- /priv/test.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: "k8s.cni.cncf.io/v1" 3 | kind: NetworkAttachmentDefinition 4 | metadata: 5 | name: test0 6 | namespace: default 7 | spec: 8 | config: '{ 9 | "cniVersion": "0.3.1", 10 | "type": "bridge", 11 | "bridge": "br-test0", 12 | "ipam": { } 13 | }' 14 | --- 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | labels: 19 | run: vxlan-test 20 | name: vxlan-test 21 | namespace: default 22 | spec: 23 | replicas: 20 24 | selector: 25 | selector: 26 | matchLabels: 27 | run: vxlan-test 28 | template: 29 | metadata: 30 | labels: 31 | run: vxlan-test 32 | vxlan.openvnf.org: "true" 33 | vxlan.travelping.com: "true" 34 | annotations: 35 | vxlan.travelping.com/networks: test0 36 | k8s.v1.cni.cncf.io/networks: default/test0@test0 37 | spec: 38 | initContainers: 39 | - image: openvnf/kube-vxlan-controller-agent 40 | resources: 41 | limits: 42 | cpu: "1" 43 | memory: "200Mi" 44 | name: vxlan-controller-agent-init 45 | securityContext: 46 | capabilities: 47 | add: 48 | - NET_ADMIN 49 | containers: 50 | - image: aialferov/pause 51 | imagePullPolicy: Always 52 | name: a 53 | - image: openvnf/kube-vxlan-controller-agent 54 | name: vxlan-controller-agent 55 | securityContext: 56 | capabilities: 57 | add: 58 | - NET_ADMIN 59 | -------------------------------------------------------------------------------- /src/kube_vxlan_controller_sup.erl: -------------------------------------------------------------------------------- 1 | -module(kube_vxlan_controller_sup). 2 | 3 | -behaviour(supervisor). 4 | 5 | -include_lib("kernel/include/logger.hrl"). 6 | 7 | -define(Run, kube_vxlan_controller_run). 8 | -define(K8s, kube_vxlan_controller_k8s). 9 | -define(PodReg, kube_vxlan_controller_pod_reg). 10 | -define(PodSup, kube_vxlan_controller_pod_sup). 11 | 12 | %% API 13 | -export([start_link/1]). 14 | 15 | %% Supervisor callbacks 16 | -export([init/1]). 17 | 18 | %% Helper macro for declaring children of supervisor 19 | -define(CHILD(I, Type, Args), #{id => I, 20 | start => {I, start_link, Args}, 21 | restart => permanent, 22 | shutdown => 5000, 23 | type => Type, 24 | modules => [I]}). 25 | 26 | %% =================================================================== 27 | %% API functions 28 | %% =================================================================== 29 | 30 | start_link(Config) -> 31 | ?LOG(info, "starting ~p with ~p", [?MODULE, Config]), 32 | supervisor:start_link({local, ?MODULE}, ?MODULE, [Config]). 33 | 34 | %% =================================================================== 35 | %% Supervisor callbacks 36 | %% =================================================================== 37 | 38 | init([Config]) -> 39 | SupFlags = 40 | #{strategy => one_for_one, 41 | intensity => 5, 42 | period => 10}, 43 | {ok, {SupFlags, 44 | [ 45 | ?CHILD(?PodSup, supervisor, []), 46 | ?CHILD(?PodReg, worker, []), 47 | ?CHILD(?K8s, worker, [Config]), 48 | ?CHILD(?Run, worker, [Config]) 49 | ]}}. 50 | -------------------------------------------------------------------------------- /src/kube_vxlan_controller_db.erl: -------------------------------------------------------------------------------- 1 | -module(kube_vxlan_controller_db). 2 | 3 | %% API 4 | -export([nets_options/0, load_db/1]). 5 | 6 | 7 | -include_lib("kernel/include/logger.hrl"). 8 | 9 | -define(K8s, kube_vxlan_controller_k8s). 10 | -define(Tools, kube_vxlan_controller_tools). 11 | 12 | -define(TABLENAME, ?MODULE). 13 | 14 | nets_options() -> 15 | persistent_term:get(?TABLENAME, #{}). 16 | 17 | load_db(#{namespace := Namespace, 18 | configmap_name := ConfigMapName} = Config) -> 19 | Resource = "/api/v1/namespaces/" ++ Namespace ++ 20 | "/configmaps/" ++ ConfigMapName, 21 | Db = 22 | case ?K8s:http_request(Resource, [], Config) of 23 | {ok, #{data := Data}} -> 24 | ?LOG(debug, "load VXLAN config ~p", [Data]), 25 | maps:fold(fun(NetName, NetOptions, Map) -> 26 | maps:put(atom_to_list(NetName), net_options(NetOptions), Map) 27 | end, #{}, Data); 28 | _ -> 29 | #{} 30 | end, 31 | persistent_term:put(?TABLENAME, Db). 32 | 33 | %%%========================================================================= 34 | %%% internal functions 35 | %%%========================================================================= 36 | 37 | net_options(Options) -> 38 | maps:from_list(net_id_bc([ 39 | ?Tools:pod_read_net_option(Option) || 40 | Option <- string:lexemes(binary_to_list(Options), " ") 41 | ])). 42 | 43 | %%% TODO: provided for BC, remove when not needed 44 | net_id_bc(Options) -> 45 | lists:map(fun({Name, Value}) -> 46 | NameString = atom_to_list(Name), 47 | try list_to_integer(NameString) of 48 | Id -> {id, integer_to_list(Id)} 49 | catch 50 | _:_ -> {Name, Value} 51 | end 52 | end, Options). 53 | %%% 54 | -------------------------------------------------------------------------------- /src/kube_vxlan_controller_state.erl: -------------------------------------------------------------------------------- 1 | -module(kube_vxlan_controller_state). 2 | 3 | -export([ 4 | is/3, 5 | set/3, 6 | unset/3, 7 | 8 | resource_version/1, 9 | set_resource_version/2, 10 | is_resource_version_shown/1, 11 | set_resource_version_shown/1, 12 | set_resource_version_unshown/1 13 | ]). 14 | 15 | is(Event, #{pod_uid := Uid}, State) -> 16 | sets:is_element(Uid, event(Event, State)). 17 | 18 | set(Event, #{pod_uid := Uid}, State) -> 19 | maps:put(Event, sets:add_element(Uid, event(Event, State)), State). 20 | 21 | unset(Event, #{pod_uid := Uid}, State) -> 22 | maps:put(Event, sets:del_element(Uid, event(Event, State)), State). 23 | 24 | event(Event, State) -> maps:get(Event, State, sets:new()). 25 | 26 | resource_version(State) -> 27 | maps:get(resource_version, State, "0"). 28 | 29 | set_resource_version(Version, State) when is_integer(Version) -> 30 | maps:put(resource_version, Version, State); 31 | set_resource_version(#{resource_version := Value}, State) 32 | when is_integer(Value) -> 33 | update_resource_version(Value, State); 34 | set_resource_version(#{resourceVersion := Value}, State) 35 | when is_binary(Value) -> 36 | update_resource_version(binary_to_integer(Value), State). 37 | 38 | update_resource_version(Value, State) -> 39 | OldValue = resource_version(State), 40 | ProposedValue = Value, 41 | 42 | case ProposedValue >= OldValue of 43 | true -> 44 | NewValue = ProposedValue + 1, 45 | NewState = set_resource_version_unshown(State), 46 | set_resource_version(NewValue, NewState); 47 | false -> State 48 | end. 49 | 50 | is_resource_version_shown(State) -> 51 | maps:get(resource_version_shown, State, false). 52 | 53 | set_resource_version_shown(State) -> 54 | maps:put(resource_version_shown, true, State). 55 | 56 | set_resource_version_unshown(State) -> 57 | maps:put(resource_version_shown, false, State). 58 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: bionic 3 | services: 4 | - docker 5 | git: 6 | depth: false 7 | 8 | language: erlang 9 | 10 | otp_release: 11 | - 22.3.4 12 | - 23.0.2 13 | 14 | install: "true" 15 | 16 | env: 17 | global: 18 | - BUILD_IMAGE="openvnf/kube-vxlan-controller" 19 | - DOCKER_USERNAME="openvnfdockhub" 20 | - secure: "fn9gwy919gluWuCHRtrBd2ggrkVS3SaMVIN3+vJHi8ZpT5d5pnmJ0xD74LiRkL4rcmn4+d3obcSu6lBpyPzi3NJ2FdDXDGBrkpGqfX4WbElMe8pD9+bEZiaONF3RnC1+1VQ+6nMjrFdIDgXomkFjRW7zK4YNTxTqLOYVr4k4i6htYD7MeUwEU31P5as5KYlqAxckamIHS8M/DHijTyTnVCxTrEjbPc/AFF37tBTieyFEyJaUfYtfZ3YM84obWiD4odAsMPX8YLCirgjpz+eyjgdeA7967FmUuuPZjHyzhnBQf1bmEDQG7VVFvjxlcE8BYPrLMv/SRdK+pm5ouL/Wlim8ACPAurH9/xiyisaG1mTEb9h+aEkHXVzW6G3ciYHgPh/OagceZLnW+AyUep4O7Wu9lxdTeMrQ0cAvETOJWQqYlqYwyCYlOKiVRlnEAe0dk6tmTcSMd+jSp0eq5nWKRC2/fGkje3hyWvK7xGKTn6ekZUCDiNidhMbYq+Y9p0M4gw/xNa6z+u17GSNXtZVpvqUF7MQ6GQk9CKeXPz8fyCTg57xUa/BTA3W0twiItVzZRwQB/bN08VPSjuJKde2OxjXD0WQB2j9NCyW3veh7OS3AxxClZNQ1lwcnWP4zc4b/P76NObU+UpIoWILYSvkohUdD62h4McDc9OYks8qMaN8=" 21 | 22 | before_script: 23 | - wget https://s3.amazonaws.com/rebar3/rebar3 24 | - chmod u+x ./rebar3 25 | 26 | script: 27 | - ./rebar3 compile 28 | - ./rebar3 do xref 29 | 30 | jobs: 31 | include: 32 | - 33 | - stage: docker 34 | otp_release: 23.0.2 35 | script: 36 | - docker build -t $BUILD_IMAGE -f docker/Dockerfile . 37 | after_success: 38 | - docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD" 39 | - export GIT_DESCRIBE=`git describe` 40 | - export TAG=`if [ "$TRAVIS_EVENT_TYPE" == "pull_request" ]; then echo PR-$TRAVIS_PULL_REQUEST_BRANCH\_$TRAVIS_PULL_REQUEST_SHA ; else echo $TRAVIS_BRANCH\_$GIT_DESCRIBE ; fi` 41 | - export TAG=`echo "$TAG" | sed -e 's,/,-,g'` 42 | - echo "Docker image $BUILD_IMAGE:$TAG" 43 | - if [ "$TAG" == "master" ]; then export TAG="latest"; fi 44 | - docker tag $BUILD_IMAGE $BUILD_IMAGE:$TAG 45 | - docker push $BUILD_IMAGE:$TAG 46 | - echo "docker push done" 47 | -------------------------------------------------------------------------------- /src/kube_vxlan_controller_agent.erl: -------------------------------------------------------------------------------- 1 | -module(kube_vxlan_controller_agent). 2 | 3 | -export([terminate/2, exec/3]). 4 | 5 | -define(Pod, kube_vxlan_controller_pod). 6 | 7 | terminate(Pod, Config) -> 8 | %%% TODO: provided for BC, remove once not needed 9 | run(Pod, "touch /run/terminate", Config), 10 | %%% 11 | run(Pod, "kill -TERM 1", Config). 12 | 13 | exec(Pod, Command, Config) -> 14 | run(Pod, Command, Config). 15 | 16 | run(#{namespace := Namespace, name := PodName}, Command, Config) -> 17 | ContainerName = maps:get(agent_container_name, Config), 18 | ?Pod:exec(Namespace, PodName, ContainerName, Command, Config). 19 | 20 | %-define(AgentContainerName, <<"vxlan-controller-agent">>). 21 | %-define(AgentImage, <<"aialferov/kube-vxlan-controller-agent">>). 22 | 23 | %-define(AgentSpec, #{ 24 | % spec => #{ 25 | % template => #{ 26 | % spec => #{ 27 | % containers => [ 28 | % #{name => ?AgentContainerName, 29 | % image => ?AgentImage} 30 | % ]}}} 31 | %}). 32 | % 33 | %embed(Namespace, DeploymentName, Config) -> 34 | % ?LOG(info, "Embedding agent into \"~s\" deployment", [DeploymentName]), 35 | % 36 | % Resource = "/apis/apps/v1beta2/namespaces/" ++ Namespace ++ 37 | % "/deployments/" ++ binary_to_list(DeploymentName), 38 | % Headers = [ 39 | % {"Content-Type", <<"application/strategic-merge-patch+json">>} 40 | % ], 41 | % Body = jsx:encode(?AgentSpec), 42 | % 43 | % {ok, Data} = ?K8s:http_request(patch, Resource, [], Headers, Body, Config), 44 | % ?LOG(info, Data), 45 | % ok. 46 | % 47 | %get_deployment_name(Namespace, ReplicaSetName) -> 48 | % ResourceReplicaSet = "/apis/extensions/v1beta1/namespaces/" ++ Namespace ++ 49 | % "/replicasets/" ++ binary_to_list(ReplicaSetName), 50 | % {ok, [#{ 51 | % metadata := #{ 52 | % ownerReferences := [#{ 53 | % kind := <<"Deployment">>, 54 | % name := DeploymentName 55 | % }|_] 56 | % } 57 | % }]} = ?K8s:http_request(ResourceReplicaSet, [], Config), 58 | % DeploymentName. 59 | -------------------------------------------------------------------------------- /kubernetes/bundle.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: kube-vxlan-controller 5 | namespace: kube-system 6 | --- 7 | apiVersion: rbac.authorization.k8s.io/v1beta1 8 | kind: ClusterRoleBinding 9 | metadata: 10 | name: kube-vxlan-controller 11 | roleRef: 12 | apiGroup: rbac.authorization.k8s.io 13 | kind: ClusterRole 14 | name: cluster-admin 15 | subjects: 16 | - kind: ServiceAccount 17 | name: kube-vxlan-controller 18 | namespace: kube-system 19 | --- 20 | apiVersion: v1 21 | kind: ConfigMap 22 | metadata: 23 | name: kube-vxlan-controller 24 | namespace: kube-system 25 | data: 26 | vxeth0: id=100 up 27 | vxeth1: id=101 up 28 | vxeth2: id=102 up 29 | vxeth3: id=103 up 30 | --- 31 | apiVersion: v1 32 | kind: ConfigMap 33 | metadata: 34 | name: kube-vxlan-controller-config 35 | namespace: kube-system 36 | data: 37 | config: | 38 | [{'kube-vxlan-controller', [ 39 | {db_file, "/usr/share/kube-vxlan-controller/db"}, 40 | {selector, "vxlan.openvnf.org"}, 41 | {annotation, "vxlan.openvnf.org/networks"} 42 | ]}]. 43 | --- 44 | apiVersion: extensions/v1beta1 45 | kind: Deployment 46 | metadata: 47 | labels: 48 | run: kube-vxlan-controller 49 | name: kube-vxlan-controller 50 | namespace: kube-system 51 | spec: 52 | template: 53 | metadata: 54 | labels: 55 | run: kube-vxlan-controller 56 | spec: 57 | containers: 58 | - name: kube-vxlan-controller 59 | image: openvnf/kube-vxlan-controller:latest 60 | args: 61 | - run 62 | volumeMounts: 63 | - name: kube-vxlan-controller-config 64 | mountPath: /etc/kube-vxlan-controller 65 | - name: kube-vxlan-controller-db 66 | mountPath: /usr/share/kube-vxlan-controller 67 | volumes: 68 | - name: kube-vxlan-controller-config 69 | configMap: 70 | name: kube-vxlan-controller-config 71 | - name: kube-vxlan-controller-db 72 | emptyDir: {} 73 | serviceAccount: kube-vxlan-controller 74 | serviceAccountName: kube-vxlan-controller 75 | -------------------------------------------------------------------------------- /mk/Erlangbin.mk: -------------------------------------------------------------------------------- 1 | include mk/Erlanglib.mk 2 | 3 | PREFIX = usr/local 4 | 5 | BINDIR = bin 6 | BINPATH = $(DESTDIR)/$(PREFIX)/$(BINDIR) 7 | 8 | BIN_PATH = $(BASE_PATH)/bin 9 | ERL_PATH = $(BASE_PATH)/erl 10 | 11 | USAGE_PADDING = 16 12 | 13 | clean:: 14 | rm -f $(BIN_PATH)/$(PROJECT) 15 | 16 | install: 17 | mkdir -p $(BINPATH) 18 | install -p $(BIN_PATH)/$(PROJECT) $(BINPATH) 19 | 20 | uninstall: 21 | rm -f $(BINPATH)/$(PROJECT) 22 | rmdir -p $(BINPATH) 2>/dev/null || true 23 | 24 | run: 25 | $(BIN_PATH)/$(PROJECT) $(RUN_ARGS) 26 | 27 | join: 28 | erl \ 29 | -start_epmd false \ 30 | -remsh $(PROJECT)@localhost \ 31 | -sname $(PROJECT)-$$RANDOM \ 32 | -setcookie $(PROJECT) 33 | 34 | erlang: compile 35 | $(REBAR) erlang 36 | $(REBAR) unlock 37 | 38 | erlang-clean: 39 | rm -rf $(ERL_PATH) 40 | 41 | erlang-install: 42 | $(MAKE) -C $(ERL_PATH) install DESTDIR=$(DESTDIR) PREFIX=$(PREFIX) 43 | 44 | package-ready: clean erlang-clean erlang install erlang-install 45 | 46 | define usage-erlangbin-targets 47 | $(usage-erlanglib-targets) 48 | @printf '$(shell printf " %%-$(USAGE_PADDING)s %%s\\\n%.0s" {1..8})' \ 49 | install "Install \"$(BIN_PATH)/$(PROJECT)\" to \"$(BINPATH)\"" \ 50 | uninstall "Remove \"$(BINPATH)/$(PROJECT)\"" \ 51 | run "Run \"$(BIN_PATH)/$(PROJECT)\"" \ 52 | join "Attach to the running executable Erlang machine with remote shell" \ 53 | erlang "Create an Erlang release with runtime and libraries needed to run" \ 54 | erlang-clean "Remove created Erlang release" \ 55 | erlang-install \ 56 | "Install created Erlang release to \"$(DESTDIR)/$(PREFIX)\"" \ 57 | package-ready "Prepare for packaging" 58 | endef 59 | 60 | define usage-erlangbin-variables 61 | @printf '$(shell printf " %%-$(USAGE_PADDING)s %%s\\\n%.0s" {1..3})' \ 62 | RUN_ARGS "Compiled binary run arguments (current: \"$(RUN_ARGS)\")" \ 63 | DESTDIR "Compiled binary or Erlang release installation chroot (current: \"$(DESTDIR)\")" \ 64 | PREFIX "Compiled binary or Erlang release installation prefix (current: \"$(PREFIX)\")" 65 | endef 66 | 67 | define usage 68 | @echo "Usage: make [Variables]" 69 | @echo 70 | @echo "Targets" 71 | $(usage-erlangbin-targets) 72 | @echo 73 | @echo "Variables" 74 | $(usage-erlangbin-variables) 75 | endef 76 | -------------------------------------------------------------------------------- /src/kube_vxlan_controller_inspect.erl: -------------------------------------------------------------------------------- 1 | -module(kube_vxlan_controller_inspect). 2 | 3 | -export([ 4 | networks/2, 5 | nets/2 6 | ]). 7 | 8 | -define(Db, kube_vxlan_controller_db). 9 | -define(Net, kube_vxlan_controller_net). 10 | -define(Pod, kube_vxlan_controller_pod). 11 | -define(Agent, kube_vxlan_controller_agent). 12 | -define(Tools, kube_vxlan_controller_tools). 13 | -define(Format, kube_vxlan_controller_format). 14 | 15 | -define(Fields, ["pod", "net", "fdb", "dev", "route"]). 16 | 17 | networks(NetNames, Config) -> nets(NetNames, Config). 18 | 19 | nets(NetNames, Config) -> 20 | SilentConfig = maps:put(silent, true, Config), 21 | 22 | {ok, PodResources} = ?Pod:get({label, maps:get(selector, Config)}, Config), 23 | Pods = ?Tools:pods(PodResources, ?Db:nets_options(), 24 | [{with_nets, NetNames}], Config), 25 | lists:foreach(fun(NetName) -> 26 | io:format("[~s]~n", [NetName]), 27 | lists:foreach(fun(Pod) -> 28 | show_pod(Pod, NetName, SilentConfig) 29 | end, Pods) 30 | end, NetNames). 31 | 32 | show_pod(Pod = #{nets := Nets}, NetName, Config) -> 33 | PodNetOptions = proplists:get_value(NetName, Nets, false), 34 | is_map(PodNetOptions) andalso begin 35 | CommandFdb = ?Net:cmd("bridge fdb show dev ~s", [name], Pod, NetName), 36 | CommandDev = ?Net:cmd("ip -d addr show dev ~s", [name], Pod, NetName), 37 | CommandRoute = ?Net:cmd("ip route show dev ~s", [name], Pod, NetName), 38 | 39 | FormatFun = fun(Field) -> 40 | [$ |Field] ++ ": ~s~n" 41 | end, 42 | ArgFun = fun 43 | ("pod") -> ?Format:pod(Pod); 44 | ("net") -> ?Format:pod_net_options(PodNetOptions); 45 | ("fdb") -> 46 | Fdb = ?Agent:exec(Pod, CommandFdb, Config), 47 | string:trim(?Format:fdb(Fdb, 6)); 48 | ("dev") -> 49 | Dev = ?Agent:exec(Pod, CommandDev, Config), 50 | string:trim(?Format:dev(Dev, 6)); 51 | ("route") -> 52 | Route = ?Agent:exec(Pod, CommandRoute, Config), 53 | string:trim(?Format:route(Route, 8)) 54 | end, 55 | 56 | Fields = read_fields(maps:get(fields, Config)), 57 | Format = lists:flatten(lists:map(FormatFun, Fields)) ++ "~n", 58 | Args = lists:map(ArgFun, Fields), 59 | 60 | io:format(Format, Args) 61 | end. 62 | 63 | read_fields("all") -> ?Fields; 64 | read_fields(Fields) -> 65 | [Field || Field <- string:lexemes(Fields, ","), 66 | lists:member(Field, ?Fields)]. 67 | -------------------------------------------------------------------------------- /mk/Erlanglib.mk: -------------------------------------------------------------------------------- 1 | PROJECT = $(shell grep {application, src/*.script | cut -d" " -f2 | tr -d "',") 2 | VERSION = $(shell grep {vsn, src/$(PROJECT).app.src.script | cut -d\" -f2) 3 | GIT_SHA = $(shell git rev-parse HEAD | cut -c1-8) 4 | 5 | REBAR = $(shell which ./rebar3 || which rebar3) 6 | 7 | BASE_PATH = _build/default 8 | 9 | LIB_PATH = $(BASE_PATH)/lib 10 | PLUGIN_PATH = $(BASE_PATH)/plugins 11 | 12 | CT_SUITES_PATH = $(LIB_PATH)/$(PROJECT)/ebin 13 | CT_LOG_PATH = $(BASE_PATH)/ct_logs 14 | 15 | UNAME = $(shell uname) 16 | 17 | ifeq ($(UNAME), Darwin) 18 | CT_OPEN_CMD = open 19 | endif 20 | ifeq ($(UNAME), Linux) 21 | CT_OPEN_CMD = xdg-open 22 | endif 23 | 24 | USAGE_PADDING = 14 25 | 26 | compile: 27 | $(REBAR) compile 28 | $(REBAR) unlock 29 | 30 | check: 31 | $(REBAR) eunit 32 | 33 | clean:: 34 | find $(LIB_PATH) -type f \ 35 | -name \*.beam -o -name \*.app -o -name erlcinfo | xargs rm -f 36 | find $(LIB_PATH) -type d \ 37 | -name .rebar3 -o -name ebin | xargs rmdir 2>/dev/null || true 38 | find $(PLUGIN_PATH) -type f \ 39 | -name \*.beam -o -name \*.app -o -name erlcinfo | xargs rm -f 40 | find $(PLUGIN_PATH) -type d \ 41 | -name .rebar3 -o -name ebin | xargs rmdir 2>/dev/null || true 42 | 43 | distclean: 44 | rm -rf _build 45 | 46 | shell: 47 | $(REBAR) shell 48 | $(REBAR) unlock 49 | 50 | deps: 51 | $(REBAR) get-deps 52 | $(REBAR) unlock 53 | 54 | upgrade: 55 | $(REBAR) upgrade 56 | $(REBAR) unlock 57 | 58 | ct: 59 | $(REBAR) ct \ 60 | --dir $(CT_SUITES_PATH) \ 61 | --logdir $(CT_LOG_PATH) 62 | 63 | ct-open: 64 | $(CT_OPEN_CMD) $(CT_LOG_PATH)/index.html 65 | 66 | git-release: 67 | git tag -a $(VERSION) 68 | git push origin $(VERSION) 69 | 70 | version: 71 | @echo "Version $(VERSION) (git-$(GIT_SHA))" 72 | 73 | help: usage 74 | usage: 75 | $(usage) 76 | 77 | define usage-erlanglib-targets 78 | @printf '$(shell printf " %%-$(USAGE_PADDING)s %%s\\\n%.0s" {1..11})' \ 79 | compile \ 80 | "Compile including downloading and compiling dependencies (default)" \ 81 | check "Run EUnit based unit tests" \ 82 | clean "Remove all the compilation artefacts" \ 83 | distclean "Remove the \"_build\" directory recursively" \ 84 | shell "Run Erlang shell with all the modules compiled and loaded" \ 85 | deps "Download dependencies" \ 86 | upgrade "Upgrade dependencies" \ 87 | ct "Run Erlang Common Tests" \ 88 | ct-open "Open browser with Common Tests results" \ 89 | git-release "Create and push a git tag named after current version" \ 90 | version "Print current version and git SHA" 91 | endef 92 | 93 | define usage 94 | @echo "Usage: make " 95 | @echo 96 | @echo "Targets" 97 | $(usage-erlanglib-targets) 98 | endef 99 | -------------------------------------------------------------------------------- /kubernetes/example.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | name: a 5 | spec: 6 | template: 7 | metadata: 8 | labels: 9 | run: a 10 | vxlan.openvnf.org: "true" 11 | annotations: 12 | vxlan.openvnf.org/networks: | 13 | vxeth1 14 | ip=192.168.11.2/29 15 | route=192.168.12.0/29:192.168.11.1 16 | spec: 17 | initContainers: 18 | - image: openvnf/kube-vxlan-controller-agent 19 | name: vxlan-controller-agent-init 20 | securityContext: 21 | capabilities: 22 | add: 23 | - NET_ADMIN 24 | containers: 25 | - image: aialferov/pause 26 | imagePullPolicy: Always 27 | name: a 28 | - image: openvnf/kube-vxlan-controller-agent 29 | name: vxlan-controller-agent 30 | securityContext: 31 | capabilities: 32 | add: 33 | - NET_ADMIN 34 | --- 35 | apiVersion: extensions/v1beta1 36 | kind: Deployment 37 | metadata: 38 | name: b 39 | spec: 40 | template: 41 | metadata: 42 | labels: 43 | run: b 44 | vxlan.openvnf.org: "true" 45 | annotations: 46 | vxlan.openvnf.org/networks: | 47 | vxeth2 48 | ip=192.168.12.2/29 49 | route=192.168.11.0/29:192.168.12.1 50 | spec: 51 | initContainers: 52 | - image: openvnf/kube-vxlan-controller-agent 53 | name: vxlan-controller-agent-init 54 | securityContext: 55 | capabilities: 56 | add: 57 | - NET_ADMIN 58 | containers: 59 | - image: aialferov/pause 60 | imagePullPolicy: Always 61 | name: b 62 | - image: openvnf/kube-vxlan-controller-agent 63 | name: vxlan-controller-agent 64 | securityContext: 65 | capabilities: 66 | add: 67 | - NET_ADMIN 68 | --- 69 | apiVersion: extensions/v1beta1 70 | kind: Deployment 71 | metadata: 72 | name: gw 73 | spec: 74 | template: 75 | metadata: 76 | labels: 77 | run: gw 78 | vxlan.openvnf.org: "true" 79 | annotations: 80 | vxlan.openvnf.org/networks: | 81 | vxeth1 ip=192.168.11.1/29 82 | vxeth2 ip=192.168.12.1/29 83 | spec: 84 | initContainers: 85 | - image: openvnf/kube-vxlan-controller-agent 86 | name: vxlan-controller-agent-init 87 | securityContext: 88 | capabilities: 89 | add: 90 | - NET_ADMIN 91 | containers: 92 | - image: aialferov/pause 93 | imagePullPolicy: Always 94 | name: gw 95 | - image: openvnf/kube-vxlan-controller-agent 96 | name: vxlan-controller-agent 97 | securityContext: 98 | capabilities: 99 | add: 100 | - NET_ADMIN 101 | -------------------------------------------------------------------------------- /src/kube_vxlan_controller_config.erl: -------------------------------------------------------------------------------- 1 | -module(kube_vxlan_controller_config). 2 | 3 | -export([ 4 | load/0, load/1, 5 | 6 | init/0, 7 | 8 | read/1, 9 | build/1, 10 | validate/1, 11 | 12 | version/0 13 | ]). 14 | 15 | -include_lib("kernel/include/logger.hrl"). 16 | 17 | -define(App, 'kube-vxlan-controller'). 18 | 19 | -define(OsEnvPrefix, "KVC_"). 20 | 21 | -define(ConfigKeys, [ 22 | server, 23 | ca_cert_file, 24 | token, 25 | token_file, 26 | namespace, 27 | namespace_file, 28 | selector, 29 | annotation, 30 | configmap_name, 31 | agent_container_name, 32 | agent_init_container_name, 33 | db_file, 34 | fields 35 | ]). 36 | 37 | load() -> load(#{}). 38 | load(Args) -> cpf_funs:apply_while([ 39 | {init, fun init/0, []}, 40 | {read, fun read/1, [Args]}, 41 | {build, fun build/1, [{read}]}, 42 | {validate, fun validate/1, [{build}]} 43 | ]). 44 | 45 | init() -> cpf_env:load(application:get_env(?App, config_files, [])). 46 | 47 | read(Args) -> lists:foldl(fun read_args/2, Args, ?ConfigKeys). 48 | 49 | read_args(Key, Config) -> 50 | case maps:find(Key, Config) of 51 | {ok, Value} -> maps:put(Key, {Value, arg}, Config); 52 | error -> read_env(Key, Config) 53 | end. 54 | 55 | read_env(Key, Config) -> 56 | case os:getenv(?OsEnvPrefix ++ string:uppercase(atom_to_list(Key))) of 57 | Value when is_list(Value) -> maps:put(Key, {Value, env}, Config); 58 | false -> read_cfg(Key, Config) 59 | end. 60 | 61 | read_cfg(Key, Config) -> 62 | case application:get_env(?App, Key) of 63 | {ok, Value} -> maps:put(Key, {Value, cfg}, Config); 64 | undefined -> Config 65 | end. 66 | 67 | build(Config) -> cpf_funs:apply_while([ 68 | {annotation, fun build/2, [annotation, unsource(Config)]}, 69 | {token, fun build/2, [token, {annotation}]}, 70 | {namespace, fun build/2, [namespace, {token}]} 71 | ]). 72 | 73 | build(annotation, Config) -> 74 | maps:put(annotation, list_to_atom(maps:get(annotation, Config)), Config); 75 | 76 | build(token, Config) -> build_from_file(token, Config); 77 | build(namespace, Config) -> build_from_file(namespace, Config). 78 | 79 | validate(Config) -> {ok, Config}. 80 | 81 | unsource(Config) -> maps:map(fun 82 | (_Key, {Value, _Source}) -> Value; 83 | (_Key, Value) -> Value 84 | end, Config). 85 | 86 | build_from_file(Key, Config) -> 87 | FileKey = list_to_atom(atom_to_list(Key) ++ "_file"), 88 | case maps:is_key(Key, Config) of 89 | true -> {ok, maps:remove(FileKey, Config)}; 90 | false -> case maps:find(FileKey, Config) of 91 | {ok, FileName} -> 92 | NewConfig = maps:remove(FileKey, Config), 93 | maps_put_file(Key, FileName, NewConfig); 94 | error -> {ok, Config} 95 | end 96 | end. 97 | 98 | version() -> 99 | {ok, Vsn} = application:get_key(?App, vsn), 100 | {ok, GitSha} = application:get_env(?App, git_sha), 101 | {Vsn, GitSha}. 102 | 103 | maps_put_file(Key, FileName, Map) -> 104 | case file:read_file(FileName) of 105 | {ok, Binary} -> {ok, maps:put(Key, binary_to_list(Binary), Map)}; 106 | {error, Reason} -> {error, {Reason, FileName}} 107 | end. 108 | -------------------------------------------------------------------------------- /mk/Docker.mk: -------------------------------------------------------------------------------- 1 | REGISTRY = docker.io 2 | 3 | IMAGE = $(REGISTRY)/$(USER)/$(PROJECT):$(VERSION) 4 | IMAGE_LATEST = $(REGISTRY)/$(USER)/$(PROJECT):latest 5 | 6 | DOCKER_RUN_ARGS = -it --rm --name $(PROJECT) 7 | DOCKER_BUILD_ARGS = . -t $(IMAGE) 8 | 9 | DOCKER_USAGE_PADDING = 24 10 | 11 | docker-build: 12 | docker build $(DOCKER_BUILD_ARGS_EXTRA) $(DOCKER_BUILD_ARGS) 13 | 14 | docker-push: 15 | docker push $(IMAGE) 16 | 17 | docker-release: docker-local-release docker-push 18 | docker push $(IMAGE_LATEST) 19 | 20 | docker-local-release: 21 | docker tag $(IMAGE) $(IMAGE_LATEST) 22 | 23 | docker-run: 24 | docker run $(DOCKER_RUN_ARGS) $(DOCKER_RUN_ARGS_EXTRA) $(IMAGE) $(RUN_ARGS) 25 | 26 | docker-run-d: 27 | docker run -d $(DOCKER_RUN_ARGS) $(DOCKER_RUN_ARGS_EXTRA) $(IMAGE) $(RUN_ARGS) 28 | 29 | docker-stop: 30 | docker stop $(DOCKER_STOP_ARGS) $(PROJECT) 31 | 32 | docker-join: 33 | docker exec \ 34 | -it $(PROJECT) \ 35 | erl \ 36 | -start_epmd false \ 37 | -remsh $(PROJECT)@localhost \ 38 | -sname $(PROJECT)-$$RANDOM \ 39 | -setcookie $(PROJECT) 40 | 41 | docker-shell: 42 | docker exec -it $(PROJECT) sh 43 | 44 | docker-attach: 45 | docker attach $(PROJECT) 46 | 47 | docker-logs: 48 | docker logs $(PROJECT) 49 | 50 | docker-logs-f: 51 | docker logs -f $(PROJECT) 52 | 53 | docker-clean: 54 | docker system prune -f --filter label=project=$(PROJECT) 55 | 56 | docker-distclean: docker-clean 57 | docker rmi $(IMAGE_LATEST) $(IMAGE) 2>/dev/null || true 58 | 59 | docker-help: docker-usage 60 | docker-usage: 61 | @echo "Usage: make [Variables]" 62 | @echo 63 | @echo "Targets" 64 | $(usage-docker-targets) 65 | @echo 66 | @echo "Variables" 67 | $(usage-docker-variables) 68 | 69 | define usage-docker-targets 70 | @printf \ 71 | '$(shell printf " %%-$(DOCKER_USAGE_PADDING)s %%s\\\n%.0s" {1..14})'\ 72 | docker-build "Build image \"$(IMAGE)\"" \ 73 | docker-push "Push image \"$(IMAGE)\"" \ 74 | docker-local-release "Tag \"$(IMAGE)\" as \"$(IMAGE_LATEST)\"" \ 75 | docker-release "Same as \"docker-local-release\" and push both images" \ 76 | docker-run "Run \"$(IMAGE)\" as container \"$(PROJECT)\"" \ 77 | docker-run-d "Same as \"docker-run\" but run in background" \ 78 | docker-stop "Stop and remove running container \"$(PROJECT)\"" \ 79 | docker-join \ 80 | "Remsh to Erlang application in running container \"$(PROJECT)\"" \ 81 | docker-shell "Exec shell in running container \"$(PROJECT)\"" \ 82 | docker-attach "Attach to running container \"$(PROJECT)\"" \ 83 | docker-logs "Print running \"$(PROJECT)\" container logs" \ 84 | docker-logs-f "Same as \"docker-logs\" but follow log output" \ 85 | docker-clean "Prune everything with label \"project=$(PROJECT)\"" \ 86 | docker-distclean "Remove images \"$(IMAGE)\" and \"$(IMAGE_LATEST)\"" 87 | endef 88 | 89 | define usage-docker-variables 90 | @printf \ 91 | '$(shell printf " %%-$(DOCKER_USAGE_PADDING)s %%s\\\n%.0s" {1..9})' \ 92 | REGISTRY "Docker registry (current: \"$(REGISTRY)\")" \ 93 | USER "Used as Docker ID (current: \"$(USER)\")" \ 94 | PROJECT \ 95 | "Used as image and running container name (current: \"$(PROJECT)\")" \ 96 | VERSION "Version (current: \"$(VERSION)\")" \ 97 | RUN_ARGS "Container entrypoint arguments (current: \"$(RUN_ARGS)\")" \ 98 | DOCKER_RUN_ARGS \ 99 | "Container running arguments (current: \"$(DOCKER_RUN_ARGS)\")" \ 100 | DOCKER_RUN_ARGS_EXTRA \ 101 | "Appends to DOCKER_RUN_ARGS (current: \"$(DOCKER_RUN_ARGS_EXTRA)\")" \ 102 | DOCKER_BUILD_ARGS \ 103 | "Image building arguments (current: \"$(DOCKER_BUILD_ARGS)\")" \ 104 | DOCKER_BUILD_ARGS_EXTRA \ 105 | "Prepends to DOCKER_BUILD_ARGS (current: \"$(DOCKER_BUILD_ARGS_EXTRA)\")" 106 | endef 107 | -------------------------------------------------------------------------------- /src/kube_vxlan_controller_pod_reg.erl: -------------------------------------------------------------------------------- 1 | -module(kube_vxlan_controller_pod_reg). 2 | 3 | -behaviour(regine_server). 4 | 5 | %% API 6 | -export([start_link/0]). 7 | -export([register/2, unregister/1, lookup/1, set/2, unset/1]). 8 | -export([process_event/3, clear/0, all/0, all/1]). 9 | 10 | %% regine_server callbacks 11 | -export([init/1, handle_register/4, handle_unregister/3, handle_pid_remove/3, 12 | handle_death/3, handle_call/3, terminate/2]). 13 | 14 | %% -------------------------------------------------------------------- 15 | %% Include files 16 | %% -------------------------------------------------------------------- 17 | 18 | -include_lib("stdlib/include/ms_transform.hrl"). 19 | -include_lib("kernel/include/logger.hrl"). 20 | -include("include/kube_vxlan_controller.hrl"). 21 | 22 | -define(SERVER, ?MODULE). 23 | -define(Pod, kube_vxlan_controller_pod). 24 | -define(PodSup, kube_vxlan_controller_pod_sup). 25 | 26 | %%%=================================================================== 27 | %%% API 28 | %%%=================================================================== 29 | 30 | start_link() -> 31 | regine_server:start_link({local, ?SERVER}, ?MODULE, []). 32 | 33 | register(Key, Pid) when is_pid(Pid) -> 34 | regine_server:register(?SERVER, Pid, Key, undefined). 35 | unregister(Key) -> 36 | regine_server:unregister(?SERVER, Key, undefined). 37 | 38 | set(Key, Value) -> 39 | regine_server:call(?SERVER, {set, Key, Value}). 40 | 41 | unset(Key) -> 42 | regine_server:call(?SERVER, {set, Key, undefined}). 43 | 44 | lookup(Key) -> 45 | case ets:lookup(?SERVER, Key) of 46 | [{Key, Pid, _}] -> 47 | {ok, Pid}; 48 | _ -> 49 | {error, not_found} 50 | end. 51 | 52 | clear() -> 53 | regine_server:call(?SERVER, clear). 54 | 55 | all(Cycle) -> 56 | Ms = ets:fun2ms(fun({#uid{cycle = C}, _, Pod}) when C =:= Cycle, is_map(Pod) -> Pod end), 57 | ets:select(?SERVER, Ms). 58 | 59 | all() -> 60 | ets:tab2list(?SERVER). 61 | 62 | process_event(Id, Event, Config) -> 63 | case lookup(Id) of 64 | {ok, Pid} -> 65 | Pid ! Event; 66 | _ -> 67 | regine_server:call(?SERVER, {process_event, Id, Event, Config}) 68 | end. 69 | 70 | 71 | %%%=================================================================== 72 | %%% regine callbacks 73 | %%%=================================================================== 74 | 75 | init([]) -> 76 | ets:new(?SERVER, [ordered_set, named_table, public, {keypos, 1}]), 77 | {ok, #{}}. 78 | 79 | handle_register(Pid, Id, Value, State) -> 80 | case ets:insert_new(?SERVER, {Id, Pid, Value}) of 81 | true -> {ok, [Id], State}; 82 | false -> {error, duplicate} 83 | end. 84 | 85 | handle_unregister(Key, _Value, State) -> 86 | unregister(Key, State). 87 | 88 | handle_pid_remove(_Pid, Keys, State) -> 89 | lists:foreach(fun(Key) -> ets:delete(?SERVER, Key) end, Keys), 90 | maps:without(Keys, State). 91 | 92 | handle_death(_Pid, _Reason, State) -> 93 | State. 94 | 95 | handle_call({set, Key, Value}, _From, State) -> 96 | Result = ets:update_element(?SERVER, Key, {3, Value}), 97 | {reply, Result, State}; 98 | 99 | handle_call(clear, _From, State) -> 100 | [?Pod:stop(Pid) || {_, Pid, _} <- ets:tab2list(?SERVER)], 101 | ets:delete_all_objects(?SERVER), 102 | {reply, ok, State}; 103 | 104 | handle_call({process_event, Id, Event, Config}, _From, State) -> 105 | case lookup(Id) of 106 | {ok, Pid} -> 107 | Pid ! Event, 108 | {reply, ok, State}; 109 | _Other -> 110 | start_pod(Id, Event, Config, State) 111 | end. 112 | 113 | terminate(_Reason, _State) -> 114 | ok. 115 | 116 | %%%=================================================================== 117 | %%% Internal functions 118 | %%%=================================================================== 119 | 120 | start_pod(Id, Event, Config, State) -> 121 | case ?PodSup:start_pod(Id, Event, Config) of 122 | {ok, Pid} -> 123 | {reply, ok, State, [{register, Pid, Id, undefined}]}; 124 | Other -> 125 | ?LOG(debug, "failed to start pod for ~p with ~p", [Id, Other]), 126 | {reply, Other, State} 127 | end. 128 | 129 | unregister(Key, State) -> 130 | Pids = [Pid || {_, Pid, _} <- ets:take(?SERVER, Key)], 131 | {Pids, maps:remove(Key, State)}. 132 | -------------------------------------------------------------------------------- /src/kube_vxlan_controller_tools.erl: -------------------------------------------------------------------------------- 1 | -module(kube_vxlan_controller_tools). 2 | 3 | -include_lib("kernel/include/logger.hrl"). 4 | 5 | -export([ 6 | pods/2, 7 | pods/4, 8 | pod/4, 9 | 10 | pod_nets_data/2, 11 | pod_nets/2, 12 | 13 | pod_read_net_option/1, 14 | pod_container_state/2 15 | ]). 16 | 17 | -define(NetType, "vxlan"). 18 | -define(NetDev, "eth0"). 19 | 20 | pods(Pods, Filters) -> 21 | lists:filtermap( 22 | fun(Pod) -> pod_apply_filters(Pod, Filters) end, Pods). 23 | 24 | pods(PodResources, GlobalNetsOptions, Filters, Config) -> 25 | lists:filtermap(fun(PodResource) -> 26 | pod(PodResource, GlobalNetsOptions, Filters, Config) 27 | end, PodResources). 28 | 29 | pod(#{ 30 | metadata := #{ 31 | namespace := Namespace, 32 | name := PodName, 33 | annotations := Annotations 34 | }, 35 | status := Status 36 | }, GlobalNetsOptions, Filters, Config) -> 37 | IsAgentRunning = is_agent_running(Status, Config), 38 | IsAgentRunning andalso begin 39 | NetsData = pod_nets_data(Annotations, Config), 40 | Pod = #{ 41 | namespace => binary_to_list(Namespace), 42 | name => binary_to_list(PodName), 43 | ip => binary_to_list(maps:get(podIP, Status, <<>>)), 44 | nets => pod_nets(NetsData, GlobalNetsOptions) 45 | }, 46 | pod_apply_filters(Pod, Filters) 47 | end. 48 | 49 | pod_apply_filters(Pod, Filters) -> 50 | lists:foldl(fun pod_apply_filter/2, {true, Pod}, Filters). 51 | 52 | pod_apply_filter({with_pods, PodNames}, {true, Pod}) -> 53 | IsMember = lists:member(maps:get(name, Pod), PodNames), 54 | IsMember andalso {true, Pod}; 55 | 56 | pod_apply_filter({without_pods, PodNames}, {true, Pod}) -> 57 | IsNotMember = not lists:member(maps:get(name, Pod), PodNames), 58 | IsNotMember andalso {true, Pod}; 59 | 60 | pod_apply_filter({with_nets, NetNames}, {true, Pod}) -> 61 | PodNets = [Net || Net = {NetName, _NetOptions} <- maps:get(nets, Pod), 62 | lists:member(NetName, NetNames)], 63 | PodNets /= [] andalso {true, maps:put(nets, PodNets, Pod)}; 64 | 65 | pod_apply_filter(_Filter, false) -> false. 66 | 67 | pod_nets_data(Annotations, Config) -> 68 | binary_to_list(maps:get(maps:get(annotation, Config), Annotations, <<>>)). 69 | 70 | pod_nets(NetsData, GlobalNetsOptions) -> 71 | TokensString = re:replace(NetsData, "\\h*,\\h*", ",", 72 | [global, {return, list}]), 73 | Tokens = string:lexemes(TokensString, ",\n"), 74 | Nets = lists:reverse(lists:foldl(fun pod_nets_build/2, [], Tokens)), 75 | pod_nets_apply_global_options(GlobalNetsOptions, Nets). 76 | 77 | pod_nets_build(Token = [$ |_], [Net|Nets]) -> 78 | [pod_net_add_options(string:lexemes(Token, " "), Net)|Nets]; 79 | 80 | pod_nets_build(Token, Nets) -> 81 | [NetName|NetOptions] = string:lexemes(Token, " "), 82 | [pod_net_add_options(NetOptions, pod_net_new(NetName))|Nets]. 83 | 84 | pod_net_new(NetName) -> 85 | {NetName, #{ 86 | name => NetName, 87 | type => ?NetType, 88 | dev => ?NetDev 89 | }}. 90 | 91 | pod_net_add_options(Options, Net) -> 92 | lists:foldl(fun pod_net_add_option/2, Net, Options). 93 | 94 | pod_net_add_option(Option, {NetName, NetOptions}) -> 95 | {OptionName, OptionValue} = pod_read_net_option(Option), 96 | {NetName, maps:put(OptionName, OptionValue, NetOptions)}. 97 | 98 | pod_read_net_option(Option) -> 99 | [Name|Value] = string:split(Option, "="), 100 | {list_to_atom(Name), case Value of 101 | [] -> true; 102 | ["true"] -> true; 103 | ["false"] -> false; 104 | Value -> lists:flatten(Value) 105 | end}. 106 | 107 | pod_nets_apply_global_options(GlobalNetsOptions, Nets) -> 108 | lists:filtermap(fun({NetName, NetOptions}) -> 109 | GlobalNetOptions = maps:get(NetName, GlobalNetsOptions, #{}), 110 | MergedNetOptions = maps:merge(GlobalNetOptions, NetOptions), 111 | maps:is_key(id, MergedNetOptions) andalso 112 | {true, {NetName, MergedNetOptions}} 113 | end, Nets). 114 | 115 | is_agent_running(Status, Config) -> 116 | ContainerName = maps:get(agent_container_name, Config), 117 | ContainerStatuses = maps:get(containerStatuses, Status, []), 118 | pod_container_state(ContainerName, ContainerStatuses) == running. 119 | 120 | pod_container_state(_Name, []) -> unknown; 121 | pod_container_state(Name, Statuses) -> 122 | case [maps:keys(maps:get(state, Status)) || Status <- Statuses, 123 | maps:get(name, Status) == list_to_binary(Name)] 124 | of 125 | [[State]] -> State; 126 | [] -> unknown 127 | end. 128 | -------------------------------------------------------------------------------- /priv/admin_bin: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # /bin/sh on Solaris is not a POSIX compatible shell, but /usr/bin/ksh is. 4 | if [ `uname -s` = 'SunOS' -a "${POSIX_SHELL}" != "true" ]; then 5 | POSIX_SHELL="true" 6 | export POSIX_SHELL 7 | # To support 'whoami' add /usr/ucb to path 8 | PATH=/usr/ucb:$PATH 9 | export PATH 10 | exec /usr/bin/ksh $0 "$@" 11 | fi 12 | unset POSIX_SHELL # clear it so if we invoke other scripts, they run as ksh as well 13 | 14 | 15 | SCRIPT=$(readlink $0 || true) 16 | if [ -z $SCRIPT ]; then 17 | SCRIPT=$0 18 | fi; 19 | 20 | 21 | SCRIPT_DIR="$(cd `dirname "$SCRIPT"` && pwd -P)" 22 | RELEASE_ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd -P)" 23 | REL_NAME="{{ release_name }}" 24 | REL_VSN="{{ rel_vsn }}" 25 | ERTS_VSN="{{ erts_vsn }}" 26 | CODE_LOADING_MODE="${CODE_LOADING_MODE:-embedded}" 27 | REL_DIR="$RELEASE_ROOT_DIR/releases/$REL_VSN" 28 | ERL_OPTS="{{ erl_opts }}" 29 | RUNNER_LOG_DIR="${RUNNER_LOG_DIR:-$RELEASE_ROOT_DIR/log}" 30 | RUNNER_BASE_DIR=$RELEASE_ROOT_DIR 31 | RUNNER_ETC_DIR="${RUNNER_ETC_DIR:-$RELEASE_ROOT_DIR/etc}" 32 | 33 | find_erts_dir() { 34 | __erts_dir="$RELEASE_ROOT_DIR/erts-$ERTS_VSN" 35 | if [ -d "$__erts_dir" ]; then 36 | ERTS_DIR="$__erts_dir"; 37 | ROOTDIR="$RELEASE_ROOT_DIR" 38 | else 39 | __erl="$(which erl)" 40 | code="io:format(\"~s\", [code:root_dir()]), halt()." 41 | __erl_root="$("$__erl" -noshell -eval "$code")" 42 | ERTS_DIR="$__erl_root/erts-$ERTS_VSN" 43 | ROOTDIR="$__erl_root" 44 | fi 45 | } 46 | 47 | # Get node pid 48 | relx_get_pid() { 49 | if output="$(relx_nodetool rpcterms os getpid)" 50 | then 51 | echo "$output" | sed -e 's/"//g' 52 | return 0 53 | else 54 | echo "$output" 55 | return 1 56 | fi 57 | } 58 | 59 | relx_get_longname() { 60 | id="longname$(relx_gen_id)-${NAME}" 61 | "$BINDIR/erl" -boot start_clean -eval 'io:format("~s~n", [node()]), halt()' -noshell -name $id | sed -e 's/.*@//g' 62 | } 63 | 64 | # Connect to a remote node 65 | relx_rem_sh() { 66 | # Generate a unique id used to allow multiple remsh to the same node 67 | # transparently 68 | id="remsh$(relx_gen_id)-${NAME}" 69 | 70 | # Get the node's ticktime so that we use the same thing. 71 | TICKTIME="$(relx_nodetool rpcterms net_kernel get_net_ticktime)" 72 | 73 | # Setup remote shell command to control node 74 | exec "$BINDIR/erl" "$NAME_TYPE" "$id" -remsh "$NAME" -boot start_clean \ 75 | -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \ 76 | -setcookie "$COOKIE" -hidden -kernel net_ticktime $TICKTIME 77 | } 78 | 79 | # Generate a random id 80 | relx_gen_id() { 81 | od -X -N 4 /dev/urandom | head -n1 | awk '{print $2}' 82 | } 83 | 84 | # Control a node 85 | relx_nodetool() { 86 | command="$1"; shift 87 | 88 | "$ERTS_DIR/bin/escript" "$ROOTDIR/bin/nodetool" "$NAME_TYPE" "$NAME" \ 89 | -setcookie "$COOKIE" "$command" $@ 90 | } 91 | 92 | # Run an escript in the node's environment 93 | relx_escript() { 94 | shift; scriptpath="$1"; shift 95 | export RELEASE_ROOT_DIR 96 | 97 | "$ERTS_DIR/bin/escript" "$ROOTDIR/$scriptpath" $@ 98 | } 99 | 100 | # Output a start command for the last argument of run_erl 101 | relx_start_command() { 102 | printf "exec \"%s\" \"%s\"" "$RELEASE_ROOT_DIR/bin/$REL_NAME" \ 103 | "$START_OPTION" 104 | } 105 | 106 | NODE_NAME=`grep -e '-setcookie' $RUNNER_ETC_DIR/vm.args` 107 | if [ -z "$NODENAME" ]; then 108 | echo "vm.args needs to have a -name parameter." 109 | echo " -sname is not supported." 110 | exit 1 111 | else 112 | NAME_TYPE="-name" 113 | NAME="${NODENAME# *}" 114 | fi 115 | 116 | PIPE_DIR="${PIPE_DIR:-/tmp/erl_pipes/$NAME/}" 117 | 118 | # Extract the target cookie 119 | COOKIE_ARG=`grep -e '-setcookie' $RUNNER_ETC_DIR/vm.args` 120 | 121 | find_erts_dir 122 | export ROOTDIR="$RELEASE_ROOT_DIR" 123 | export BINDIR="$ERTS_DIR/bin" 124 | export EMU="beam" 125 | export PROGNAME="erl" 126 | export LD_LIBRARY_PATH="$ERTS_DIR/lib:$LD_LIBRARY_PATH" 127 | ERTS_LIB_DIR="$ERTS_DIR/../lib" 128 | 129 | cd "$ROOTDIR" 130 | 131 | # Parse out release and erts info 132 | START_ERL=`cat $RUNNER_BASE_DIR/releases/start_erl.data` 133 | ERTS_VSN=${START_ERL% *} 134 | APP_VSN=${START_ERL#* } 135 | 136 | # TODO: look in the release otherwise use which 137 | ESCRIPT=escript 138 | NODETOOL_PATH=$RUNNER_BASE_DIR/bin 139 | NODETOOL=$NODETOOL_PATH/nodetool 140 | # Setup command to control the node 141 | NODETOOL="$ESCRIPT $NODETOOL $NAME_ARG $COOKIE_ARG" 142 | 143 | ensure_node_running() 144 | { 145 | # Make sure the local node IS running 146 | if ! relx_nodetool "ping"; then 147 | echo "Node is not running!" 148 | exit 1 149 | fi 150 | } 151 | 152 | vxlan_admin() 153 | { 154 | ensure_node_running 155 | relx_nodetool rpc {{ release_name }}_cli cmd "$@" 156 | } 157 | 158 | vxlan_admin "$@" 159 | -------------------------------------------------------------------------------- /src/kube_vxlan_controller_cli.erl: -------------------------------------------------------------------------------- 1 | -module(kube_vxlan_controller_cli). 2 | 3 | -export([cli/1]). 4 | 5 | -define(List, kube_vxlan_controller_list). 6 | -define(Config, kube_vxlan_controller_config). 7 | -define(Inspect, kube_vxlan_controller_inspect). 8 | 9 | -define(Usage, 10 | "Usage: kube-vxlan-controller [Options]~n" 11 | " kube-vxlan-controller help ~n" 12 | "~n" 13 | "Commands~n" 14 | " run Run the controller~n" 15 | " list List specified resources or attributes~n" 16 | " inspect Print information about a desired entity~n" 17 | " version Print the controller version~n" 18 | "~n" ++ 19 | ?UsageOptions 20 | ). 21 | -define(UsageOptions, 22 | "Options~n" 23 | " --server=~n" 24 | " --ca-cert-file=~n" 25 | " --token=~n" 26 | " --token-file=~n" 27 | " --namespace=~n" 28 | " --namespace-file=~n" 29 | " --selector=