Page MenuHomeDevCentral

No OneTemporary

diff --git a/_modules/node.py b/_modules/node.py
index 3bde15b..3009c89 100644
--- a/_modules/node.py
+++ b/_modules/node.py
@@ -1,208 +1,223 @@
# -*- coding: utf-8 -*-
# -------------------------------------------------------------
# Salt — Node execution module
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Project: Nasqueron
# Created: 2017-10-21
# Description: Functions related to the nodes pillar entry
# License: BSD-2-Clause
# -------------------------------------------------------------
from salt.exceptions import CommandExecutionError, SaltCloudConfigError
def _get_all_nodes():
return __pillar__.get('nodes', {})
def get_all_properties(nodename=None):
"""
A function to get a node pillar configuration.
CLI Example:
salt * node.get_all_properties
"""
if nodename is None:
nodename = __grains__['id']
all_nodes = _get_all_nodes()
if nodename not in all_nodes:
raise CommandExecutionError(
SaltCloudConfigError(
"Node {0} not declared in pillar.".format(nodename)
)
)
return all_nodes[nodename]
def get(key, nodename=None):
"""
A function to get a node pillar configuration key.
CLI Example:
salt * node.get hostname
"""
return _get_property(key, nodename, None)
def _explode_key(k): return k.split(':')
def _get_first_key(k): return _explode_key(k)[0]
def _strip_first_key(k): return ':'.join(_explode_key(k)[1:])
def _get_property(key, nodename, default_value, parent=None):
if parent is None:
parent = get_all_properties(nodename)
if ':' in key:
first_key = _get_first_key(key)
if first_key in parent:
return _get_property(
_strip_first_key(key), nodename,
default_value, parent[first_key]
)
elif key in parent:
return parent[key]
return default_value
def get_list(key, nodename=None):
"""
A function to get a node pillar configuration.
Returns a list if found, or an empty list if not found.
CLI Example:
salt * node.list network:ipv4_aliases
"""
return _get_property(key, nodename, [])
def has(key, nodename=None):
"""
A function to get a node pillar configuration.
Returns a boolean, False if not found.
CLI Example:
salt * node.has network:ipv6_tunnel
"""
value = _get_property(key, nodename, False)
return bool(value)
def has_role(role, nodename=None):
"""
A function to determine if a node has the specified role.
Returns a boolean, False if not found.
CLI Example:
salt * node.has_role devserver
"""
return role in get_list('roles', nodename)
def filter_by_role(pillar_key, nodename=None):
"""
A function to filter a dictionary by roles.
The dictionary must respect the following structure:
- keys are role to check the current node against
- values are list of items
If a key '*' is also present, it will be included
for every role.
Returns a list, extending all the filtered lists.
CLI Example:
salt * node.filter_by_role web_content_sls
"""
roles = get_list('roles', nodename)
dictionary = __pillar__.get(pillar_key, {})
filtered_list = []
for role, items in dictionary.items():
if role == '*' or role in roles:
filtered_list.extend(items)
return filtered_list
def filter_by_name(pillar_key, nodename=None):
"""
A function to filter a dictionary by node name.
The dictionary must respect the following structure:
- keys are names to check the current node against
- values are list of items
If a key '*' is also present, it will be included
for every node.
Returns a list, extending all the filtered lists.
CLI Example:
salt * node.filter_by_name mars
"""
if nodename is None:
nodename = __grains__['id']
dictionary = __pillar__.get(pillar_key, {})
filtered_list = []
for name, items in dictionary.items():
if name == '*' or name == nodename:
filtered_list.extend(items)
return filtered_list
def has_web_content(content, nodename=None):
return content in filter_by_role('web_content_sls', nodename)
def get_wwwroot(nodename=None):
"""
A function to determine the wwwroot folder to use.
Returns a string depending of the FQDN.
CLI Example:
salt * node.get_wwwroot
"""
hostname = _get_property("hostname", nodename, None)
if hostname is None:
raise CommandExecutionError(
SaltCloudConfigError(
"Node {0} doesn't have a hostname property".format(nodename)
)
)
if hostname.count('.') < 2:
return "wwwroot/{0}/www".format(hostname)
fqdn = hostname.split(".")
return "wwwroot/{1}/{0}".format(
".".join(fqdn[0:-2]),
".".join(fqdn[-2:])
)
+
+
+def get_ipv6_list():
+ """
+ A function to get a list of IPv6, enclosed by [].
+
+ Returns a string depending of the IPv6 currently assigned.
+
+ CLI Example:
+
+ salt * node.get_ipv6_list
+ """
+ ipv6 = __grains__.get("ipv6")
+
+ return " ".join(["[" + ip + "]" for ip in ipv6])
diff --git a/_tests/modules/test_node.py b/_tests/modules/test_node.py
index fdd5b9d..35aa991 100644
--- a/_tests/modules/test_node.py
+++ b/_tests/modules/test_node.py
@@ -1,115 +1,127 @@
from importlib.machinery import SourceFileLoader
import unittest
salt_test_case = SourceFileLoader('salt_test_case', "salt_test_case.py").load_module()
node = SourceFileLoader('node', "../_modules/node.py").load_module()
class Testinstance(unittest.TestCase, salt_test_case.SaltTestCase):
def setUp(self):
self.initialize_mocks()
self.instance = node
self.mock_pillar('data/forests.yaml')
self.mock_grains()
self.grains['id'] = 'egladil'
def test_get_wwwroot(self):
self.assertEqual("wwwroot/lothlorien.forest/egladil",
node.get_wwwroot())
self.assertEqual("wwwroot/entwash.node/www",
node.get_wwwroot('entwash'))
def test_has_web_content(self):
self.assertTrue(node.has_web_content('.ll/carasgaladhon'))
self.assertFalse(node.has_web_content('.arda/onodlo'))
self.assertTrue(node.has_web_content('.arda/onodlo', 'entwash'))
self.assertFalse(node.has_web_content('notexisting'))
def test_filter_by_role(self):
node_key = self.grains['id']
self.assertEqual(['Caras Galadhon'],
node.filter_by_role('items_by_role'))
self.assertEqual(['Onodlo'],
node.filter_by_role('items_by_role', 'entwash'))
# No role
self.pillar['nodes'][node_key]['roles'] = []
self.assertEqual([],
node.filter_by_role('items_by_role'))
# More than one role
self.pillar['nodes'][node_key]['roles'] = [
'border',
'treecity'
]
self.assertEqual(['Caras Galadhon', 'Onodlo'],
sorted(node.filter_by_role('items_by_role')))
def test_filter_by_role_with_star(self):
node_key = self.grains['id']
self.assertEqual(['Air', 'Caras Galadhon'],
node.filter_by_role('items_by_role_with_star'))
self.assertEqual(['Air', 'Onodlo'],
node.filter_by_role(
'items_by_role_with_star',
'entwash'
))
# No role
self.pillar['nodes'][node_key]['roles'] = []
self.assertEqual(['Air'],
node.filter_by_role('items_by_role_with_star'))
# More than one role
self.pillar['nodes'][node_key]['roles'] = [
'border',
'treecity'
]
self.assertEqual(
['Air', 'Caras Galadhon', 'Onodlo'],
sorted(node.filter_by_role('items_by_role_with_star'))
)
def test_filter_by_name(self):
self.assertEqual(
['Caras Galadhon'],
node.filter_by_name('items_by_name')
)
self.assertEqual(
['Caras Galadhon'],
node.filter_by_name('items_by_name', 'egladil')
)
self.grains['id'] = 'entwash'
self.assertEqual(
[],
node.filter_by_name('items_by_name')
)
def test_filter_by_name_with_star(self):
self.assertEqual(
['Air', 'Caras Galadhon'],
node.filter_by_name('items_by_name_with_star')
)
self.assertEqual(
['Air', 'Caras Galadhon'],
node.filter_by_name('items_by_name_with_star', 'egladil')
)
self.grains['id'] = 'entwash'
self.assertEqual(
['Air'],
node.filter_by_name('items_by_name_with_star')
)
+
+ def test_get_ipv6_list(self):
+ self.grains['ipv6'] = [
+ "::1",
+ "2001:470:1f13:ce7:ca5:cade:fab:1e",
+ "2001:470:1f12:ce7::2",
+ ]
+
+ self.assertEqual(
+ "[::1] [2001:470:1f13:ce7:ca5:cade:fab:1e] [2001:470:1f12:ce7::2]",
+ node.get_ipv6_list()
+ )
diff --git a/roles/paas-docker/nginx/config.sls b/roles/paas-docker/nginx/config.sls
index 80c3e23..b8c644b 100644
--- a/roles/paas-docker/nginx/config.sls
+++ b/roles/paas-docker/nginx/config.sls
@@ -1,64 +1,84 @@
# -------------------------------------------------------------
# Salt — Provision Docker engine
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Project: Nasqueron
# Created: 2018-03-16
# License: Trivial work, not eligible to copyright
# -------------------------------------------------------------
{% from "map.jinja" import dirs with context %}
{% set containers = salt['pillar.get']('docker_containers:' + grains['id'], {}) %}
# -------------------------------------------------------------
# Base folder
#
# :: general configuration
# -------------------------------------------------------------
{{ dirs.etc }}/nginx/nginx.conf:
file.managed:
- source: salt://roles/paas-docker/nginx/files/nginx.conf
nginx_dhparams:
cmd.run:
- name: openssl dhparam -out {{ dirs.etc }}/nginx/dhparams.pem 2048
- creates: {{ dirs.etc }}/nginx/dhparams.pem
# -------------------------------------------------------------
# includes folder
#
# :: general configuration
# :: application-specific code
# -------------------------------------------------------------
{{ dirs.etc }}/nginx/includes:
file.recurse:
- source: salt://roles/paas-docker/nginx/files/includes
- dir_mode: 755
- file_mode: 644
# -------------------------------------------------------------
# vhosts folder
+#
+# :: fallback when a domain isn't found
+# :: server cover page
+# :: containers
# -------------------------------------------------------------
+{{ dirs.etc }}/nginx/vhosts:
+ file.directory:
+ - dir_mode: 755
+
+{{ dirs.etc }}/nginx/vhosts/000-fallback.conf:
+ file.managed:
+ - source: salt://roles/paas-docker/nginx/files/vhosts/base/fallback.conf
+
+{{ dirs.etc }}/nginx/vhosts/001-server.conf:
+ file.managed:
+ - source: salt://roles/paas-docker/nginx/files/vhosts/base/server.conf
+ - template: jinja
+ - context:
+ fqdn: {{ grains['fqdn'] }}
+ ipv4: {{ grains['ipv4'] | join(" ") }}
+ ipv6: "{{ salt['node.get_ipv6_list']() }}"
+
{% for service, instances in containers.items() %}
{% for instance, container in instances.items() %}
{% if 'host' in container %}
{{ dirs.etc }}/nginx/vhosts/{{ service }}/{{ instance }}.conf:
file.managed:
- source: salt://roles/paas-docker/nginx/files/vhosts/{{ service }}.conf
- - makedirs: True
- mode: 644
- template: jinja
- context:
fqdn: {{ container['host'] }}
app_port: {{ container['app_port'] }}
aliases: {{ container['aliases'] | default('', true) | join(" ") }}
# If the nginx configuration needs more key,
# pass directly the container dictionary.
args: {{ container }}
{% endif %}
{% endfor %}
{% endfor %}
diff --git a/roles/paas-docker/nginx/files/nginx.conf b/roles/paas-docker/nginx/files/nginx.conf
index dba9445..4245fc8 100644
--- a/roles/paas-docker/nginx/files/nginx.conf
+++ b/roles/paas-docker/nginx/files/nginx.conf
@@ -1,54 +1,58 @@
# -------------------------------------------------------------
# Configuration for Docker PaaS front-end nginx
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Author: Sébastien Santoro aka Dereckson
# Created: 2020-02-18
# Source file: roles/paas-docker/nginx/files/nginx.conf
# -------------------------------------------------------------
#
# <auto-generated>
# This file is managed by our rOPS SaltStack repository.
#
# Changes to this file may cause incorrect behavior
# and will be lost if the state is redeployed.
# </auto-generated>
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 1024;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
# Optimizing how packets are sent
# Reference: https://thoughts.t37.net/nginx-optimization-understanding-sendfile-tcp-nodelay-and-tcp-nopush-c55cdd276765
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
client_max_body_size 64M;
server_names_hash_bucket_size 128;
include mime.types;
default_type application/octet-stream;
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
+ # Base
+ include /etc/nginx/vhosts/000-fallback.conf;
+ include /etc/nginx/vhosts/001-server.conf;
+
# Services hosted in containers
include /etc/nginx/vhosts/*/*.conf;
}
diff --git a/roles/paas-docker/nginx/files/vhosts/base/fallback.conf b/roles/paas-docker/nginx/files/vhosts/base/fallback.conf
new file mode 100644
index 0000000..8a7b5e2
--- /dev/null
+++ b/roles/paas-docker/nginx/files/vhosts/base/fallback.conf
@@ -0,0 +1,27 @@
+# -------------------------------------------------------------
+# Configuration for Docker PaaS front-end nginx
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Author: Sébastien Santoro aka Dereckson
+# Created: 2020-02-18
+# Source file: roles/paas-docker/nginx/files/vhosts/base/fallback.conf
+# -------------------------------------------------------------
+#
+# <auto-generated>
+# This file is managed by our rOPS SaltStack repository.
+#
+# Changes to this file may cause incorrect behavior
+# and will be lost if the state is redeployed.
+# </auto-generated>
+
+server {
+ listen 80;
+ listen [::]:80;
+ server_name _;
+
+ location / {
+ root /var/wwwroot-content/_fallback;
+ index index.html;
+
+ try_files $uri $uri/ index.html;
+ }
+}
diff --git a/roles/paas-docker/nginx/files/vhosts/base/server.conf b/roles/paas-docker/nginx/files/vhosts/base/server.conf
new file mode 100644
index 0000000..f5bc858
--- /dev/null
+++ b/roles/paas-docker/nginx/files/vhosts/base/server.conf
@@ -0,0 +1,58 @@
+# -------------------------------------------------------------
+# Configuration for Docker PaaS front-end nginx
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+# Author: Sébastien Santoro aka Dereckson
+# Created: 2020-02-18
+# Source file: roles/paas-docker/nginx/files/vhosts/base/server.conf
+# -------------------------------------------------------------
+#
+# <auto-generated>
+# This file is managed by our rOPS SaltStack repository.
+#
+# Changes to this file may cause incorrect behavior
+# and will be lost if the state is redeployed.
+# </auto-generated>
+
+# -------------------------------------------------------------
+# TLS site
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+server {
+ listen 80;
+ listen [::]:80;
+ server_name {{ fqdn }};
+
+ include includes/letsencrypt;
+
+ return 301 https://{{ fqdn }}$request_uri;
+}
+
+server {
+ server_name {{ fqdn }};
+
+ include includes/tls;
+ ssl_certificate /srv/letsencrypt/etc/live/{{ fqdn }}/fullchain.pem;
+ ssl_certificate_key /srv/letsencrypt/etc/live/{{ fqdn }}/privkey.pem;
+
+ include includes/letsencrypt;
+
+ location / {
+ root /var/wwwroot-content/{{ fqdn }};
+ index index.html;
+ }
+}
+
+# -------------------------------------------------------------
+# Probably not any TLS certificate available, so serve on :80
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+server {
+ listen 80;
+ listen [::]:80;
+ server_name {{ ipv4 }} {{ ipv6 }} localhost;
+
+ location / {
+ root /var/wwwroot-content/{{ fqdn }};
+ index index.html;
+ }
+}

File Metadata

Mime Type
text/x-diff
Expires
Fri, Nov 28, 17:42 (1 d, 9 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3209073
Default Alt Text
(17 KB)

Event Timeline