Page Menu
Home
DevCentral
Search
Configure Global Search
Log In
Files
F14040509
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
17 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
Mime Type
text/x-diff
Expires
Fri, Nov 28, 17:42 (1 d, 4 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3209073
Default Alt Text
(17 KB)
Attached To
Mode
rOPS Nasqueron Operations
Attached
Detach File
Event Timeline
Log In to Comment