diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c4b5736..6730123 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,11 @@ nginx-formula ============= +0.0.4 +----- + +- Added .ng states + 0.0.3 ----- diff --git a/README.rst b/README.rst index 832f822..ccfb51f 100644 --- a/README.rst +++ b/README.rst @@ -50,3 +50,35 @@ Installs nginx via the source files. --------------- Installs apache utils, and configures nginx users specified in the pillar. + +``nginx.ng`` +------------ + +Meta-state for inclusion of all ng states. This is a reimplementation that provides basic support for vhost management. + +**Note:** nginx.ng requires the merge parameter of salt.modules.pillar.get(), first available in the Helium release. + +``nginx.ng.install`` +~~~~~~~~~~~~~~~~~~~~ + +Installs the nginx package. + +``nginx.ng.config`` +~~~~~~~~~~~~~~~~~~~ + +Manages the nginx main server configuration file. + +``nginx.ng.service`` +~~~~~~~~~~~~~~~~~~~~ + +Manages the startup and running state of the nginx service. + +``nginx.ng.vhosts_config`` +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Manages virtual host files. This state only manages the content of the files and does not bind them to service calls. + +``nginx.ng.vhosts`` +~~~~~~~~~~~~~~~~~~~ + +Manages nginx virtual hosts files and binds them to service calls. \ No newline at end of file diff --git a/VERSION b/VERSION index 6812f81..81340c7 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.0.3 \ No newline at end of file +0.0.4 diff --git a/nginx/ng/config.sls b/nginx/ng/config.sls new file mode 100644 index 0000000..c0923a5 --- /dev/null +++ b/nginx/ng/config.sls @@ -0,0 +1,14 @@ +# nginx.ng.install +# +# Manages the main nginx server configuration file. + +{% from 'nginx/ng/map.jinja' import nginx, sls_block with context %} + +nginx_config: + file.managed: + {{ sls_block(nginx.server.opts) }} + - name: {{ nginx.lookup.conf_file }} + - source: salt://nginx/ng/files/nginx.conf + - template: jinja + - context: + config: {{ nginx.server.config }} diff --git a/nginx/ng/files/nginx.conf b/nginx/ng/files/nginx.conf new file mode 100644 index 0000000..8e58e38 --- /dev/null +++ b/nginx/ng/files/nginx.conf @@ -0,0 +1,29 @@ +{% set indent_increment = 4 %} + +{%- macro nginx_block(value, key=None, operator=' ', delim=';', ind=0) -%} + {%- if value is number or value is string -%} + {{ key|indent(ind, True) }}{{ operator }}{{ value }}{{ delim }} + {%- elif value is mapping -%} + {{ key|indent(ind, True) }}{{ operator }}{{ '{' }} + {%- for k, v in value.items() %} +{{ nginx_block(v, k, operator, delim, (ind + indent_increment)) }} + {%- endfor %} +{{ '}'|indent(ind, True) }} + {%- elif value is iterable -%} + {%- for v in value %} +{{ nginx_block(v, key, operator, delim, ind) }} + {%- endfor -%} + {%- else -%} + {{ key|indent(ind, True) }}{{ operator }}{{ value }}{{ delim }} + {%- endif -%} +{%- endmacro -%} + +# Default nginx server configuration +# +# **** DO NOT EDIT THIS FILE **** +# +# This file is managed by Salt. + +{% for key, value in config.items() %} +{{ nginx_block(value, key) }} +{%- endfor -%} diff --git a/nginx/ng/files/vhost.conf b/nginx/ng/files/vhost.conf new file mode 100644 index 0000000..4daac88 --- /dev/null +++ b/nginx/ng/files/vhost.conf @@ -0,0 +1,29 @@ +{% set ind_increment = 4 %} +{%- macro vhost_config(values, key='', ind=0, lb='\n', delim=';', operator=' ') -%} + {%- for value in values -%} + {%- if value is number or value is string -%} +{{ lb }}{{ key|indent(ind, True) }}{{ operator }}{{ value }}{{ delim }} + {%- elif value is mapping -%} + {%- for k, v in value.items() -%} + {%- if v is number or v is string -%} +{{ vhost_config([v], k, ind) }} + {%- elif v|length() > 0 and (v[0] is number or v[0] is string) -%} +{{ lb }}{{ k|indent(ind,True) }}{{ vhost_config(v,'', 0, '', '')}}{{ delim }} + {%- else %} +{{ k|indent(ind, True) }} {{ '{' }} +{{- vhost_config(v, '', ind + ind_increment) }} +{{ '}'|indent(ind, True) }} + {%- endif -%} + {%- endfor -%} + {%- elif value is iterable -%} +{{ vhost_config(value, ind + ind_increment, delim, operator) }} + {%- endif -%} + {%- endfor -%} +{%- endmacro -%} + +# Nginx vhost configuration +# +# **** DO NOT EDIT THIS FILE **** +# +# This file is managed by Salt. +{{ vhost_config(config) }} diff --git a/nginx/ng/init.sls b/nginx/ng/init.sls new file mode 100644 index 0000000..4cf4ecf --- /dev/null +++ b/nginx/ng/init.sls @@ -0,0 +1,14 @@ +# nginx.ng +# +# Meta-state to fully install nginx. + +include: + - nginx.ng.config + - nginx.ng.service + - nginx.ng.vhosts + +extend: + nginx_service: + service: + - watch: + - file: nginx_config diff --git a/nginx/ng/install.sls b/nginx/ng/install.sls new file mode 100644 index 0000000..f538ccd --- /dev/null +++ b/nginx/ng/install.sls @@ -0,0 +1,14 @@ +# nginx.ng.install +# +# Manages installation of nginx. + +{% from 'nginx/ng/map.jinja' import nginx, sls_block with context %} + +nginx_install: + {% if nginx.from_source %} + ## add source compilation here + {% else %} + pkg.installed: + {{ sls_block(nginx.package.opts) }} + - name: {{ nginx.lookup.package }} + {% endif %} diff --git a/nginx/ng/map.jinja b/nginx/ng/map.jinja new file mode 100644 index 0000000..4cb455f --- /dev/null +++ b/nginx/ng/map.jinja @@ -0,0 +1,79 @@ +{% macro sls_block(dict) %} + {% for key, value in dict.items() %} + - {{ key }}: {{ value|json() }} + {% endfor %} +{% endmacro %} + +{% set nginx = salt['pillar.get']('nginx:ng', { + 'lookup': salt['grains.filter_by']({ + 'Debian': { + 'package': 'nginx', + 'service': 'nginx', + 'webuser': 'www-data', + 'conf_file': '/etc/nginx/nginx.conf', + 'vhost_available': '/etc/nginx/sites-available', + 'vhost_enabled': '/etc/nginx/sites-enabled', + 'vhost_use_symlink': True, + }, + 'RedHat': { + 'package': 'nginx', + 'service': 'nginx', + 'webuser': 'httpd', + 'conf_file': '/etc/nginx/nginx.conf', + 'vhost_available': '/etc/nginx/conf.d', + 'vhost_enabled': '/etc/nginx/conf.d', + 'vhost_use_symlink': False, + }, + }, default='Debian' ), + 'from_source': False, + 'package': { + 'opts': {}, + }, + 'service': { + 'enable': True, + 'opts': {}, + }, + 'server': { + 'opts': {}, + 'config': { + 'worker_processes': 4, + 'pid': '/run/nginx.pid', + 'events': { + 'worker_connections': 768, + }, + 'http': { + 'sendfile': 'on', + 'tcp_nopush': 'on', + 'tcp_nodelay': 'on', + 'keepalive_timeout': '65', + 'types_hash_max_size': '2048', + 'default_type': 'application/octet-stream', + 'access_log': '/var/log/nginx/access.log', + 'error_log': '/var/log/nginx/error.log', + 'gzip': 'off', + 'gzip_disable': '"msie6"', + 'include': [ + '/etc/nginx/mime.types', + '/etc/nginx/conf.d/*.conf', + '/etc/nginx/sites-enabled/*', + ], + }, + }, + }, + 'vhosts': { + 'disabled_postfix': '.disabled', + 'symlink_opts': {}, + 'rename_opts': {}, + 'managed_opts': {}, + 'dir_opts': { + 'makedirs': True, + }, + 'managed': {}, + }, +}, merge=True) %} + +{% if 'user' not in nginx.server.config %} +{% do nginx.server.config.update({ + 'user': nginx.lookup.webuser, +})%} +{% endif %} diff --git a/nginx/ng/service.sls b/nginx/ng/service.sls new file mode 100644 index 0000000..177c175 --- /dev/null +++ b/nginx/ng/service.sls @@ -0,0 +1,21 @@ +# nginx.ng.service +# +# Manages the nginx service. + +{% from 'nginx/ng/map.jinja' import nginx, sls_block with context %} +{% set service_function = {True:'running', False:'dead'}.get(nginx.service.enable) %} + +include: + - nginx.ng.install + +nginx_service: + service.{{ service_function }}: + {{ sls_block(nginx.service.opts) }} + - name: {{ nginx.lookup.service }} + - enable: {{ nginx.service.enable }} + - require: + - sls: nginx.ng.install + - watch: + {% if not nginx.from_source %} + - pkg: nginx_install + {% endif %} diff --git a/nginx/ng/vhosts.sls b/nginx/ng/vhosts.sls new file mode 100644 index 0000000..bfe67b1 --- /dev/null +++ b/nginx/ng/vhosts.sls @@ -0,0 +1,24 @@ +# nginx.ng.vhosts +# +# Manages virtual hosts and their relationship to the nginx service. + +{% from 'nginx/ng/map.jinja' import nginx, sls_block with context %} +{% from 'nginx/ng/vhosts_config.sls' import vhost_states with context %} +{% from 'nginx/ng/service.sls' import service_function with context %} + +include: + - nginx.ng.service + - nginx.ng.vhosts_config + +{% if vhost_states|length() > 0 %} +nginx_service_reload: + service.{{ service_function }}: + - name: {{ nginx.lookup.service }} + - reload: True + - use: + - service: nginx_service + - watch: + {%- for vhost in vhost_states %} + - file: {{ vhost }} + {% endfor -%} +{% endif %} diff --git a/nginx/ng/vhosts_config.sls b/nginx/ng/vhosts_config.sls new file mode 100644 index 0000000..e204d4d --- /dev/null +++ b/nginx/ng/vhosts_config.sls @@ -0,0 +1,111 @@ +# nginx.ng.vhosts_config +# +# Manages the configuration of virtual host files. + +{% from 'nginx/ng/map.jinja' import nginx, sls_block with context %} +{% set vhost_states = [] %} + +# Simple path concatenation. +# Needs work to make this function on windows. +{% macro path_join(file, root) -%} + {{ root ~ '/' ~ file }} +{%- endmacro %} + +# Retrieves the disabled name of a particular vhost +{% macro disabled_name(vhost) -%} + {%- if nginx.lookup.vhost_use_symlink -%} + {{ nginx.vhosts.managed.get(vhost).get('disabled_name', vhost) }} + {%- else -%} + {{ nginx.vhosts.managed.get(vhost).get('disabled_name', vhost ~ nginx.vhosts.disabled_postfix) }} + {%- endif -%} +{%- endmacro %} + +# Gets the path of a particular vhost +{% macro vhost_path(vhost, state) -%} + {%- if state == True -%} + {{ path_join(vhost, nginx.vhosts.managed.get(vhost).get('dir', nginx.lookup.vhost_enabled)) }} + {%- elif state == False -%} + {{ path_join(disabled_name(vhost), nginx.vhosts.managed.get(vhost).get('dir', nginx.lookup.vhost_available)) }} + {%- else -%} + {{ path_join(vhost, nginx.vhosts.managed.get(vhost).get('dir', nginx.lookup.vhost_available)) }} + {%- endif -%} +{%- endmacro %} + +# Gets the current canonical name of a vhost +{% macro vhost_curpath(vhost) -%} + {{ vhost_path(vhost, nginx.vhosts.managed.get(vhost).get('available')) }} +{%- endmacro %} + +# Creates the sls block that manages symlinking / renaming vhosts +{% macro manage_status(vhost, state) -%} + {%- set anti_state = {True:False, False:True}.get(state) -%} + {% if state == True %} + {%- if nginx.lookup.vhost_use_symlink %} + file.symlink: + {{ sls_block(nginx.vhosts.symlink_opts) }} + - name: {{ vhost_path(vhost, state) }} + - target: {{ vhost_path(vhost, anti_state) }} + {%- else %} + file.rename: + {{ sls_block(nginx.vhosts.rename_opts) }} + - name: {{ vhost_path(vhost, state) }} + - source: {{ vhost_path(vhost, anti_state) }} + {%- endif %} + {%- elif state == False %} + {%- if nginx.lookup.vhost_use_symlink %} + file.absent: + - name: {{ vhost_path(vhost, anti_state) }} + {%- else %} + file.rename: + {{ sls_block(nginx.vhosts.rename_opts) }} + - name: {{ vhost_path(vhost, state) }} + - source: {{ vhost_path(vhost, anti_state) }} + {%- endif -%} + {%- endif -%} +{%- endmacro %} + +# Makes sure the enabled directory exists +nginx_vhost_enabled_dir: + file.directory: + {{ sls_block(nginx.vhosts.dir_opts) }} + - name: {{ nginx.lookup.vhost_enabled }} + +# If enabled and available are not the same, create available +{% if nginx.lookup.vhost_enabled != nginx.lookup.vhost_available -%} +nginx_vhost_available_dir: + file.directory: + {{ sls_block(nginx.vhosts.dir_opts) }} + - name: {{ nginx.lookup.vhost_available }} +{%- endif %} + +# Manage the actual vhost files +{% for vhost, settings in nginx.vhosts.managed.items() %} +{% endfor %} + +# Managed enabled/disabled state for vhosts +{% for vhost, settings in nginx.vhosts.managed.items() %} +{% if settings.config != None %} +{% set conf_state_id = 'vhost_conf_' ~ loop.index0 %} +{{ conf_state_id }}: + file.managed: + {{ sls_block(nginx.vhosts.managed_opts) }} + - name: {{ vhost_curpath(vhost) }} + - source: salt://nginx/ng/files/vhost.conf + - template: jinja + - context: + config: {{ settings.config }} +{% do vhost_states.append(conf_state_id) %} +{% endif %} + +{% if settings.enabled != None %} +{% set status_state_id = 'vhost_state_' ~ loop.index0 %} +{{ status_state_id }}: +{{ manage_status(vhost, settings.enabled) }} +{% if settings.config != None %} + - require: + - file: {{ conf_state_id }} +{% endif %} + +{% do vhost_states.append(status_state_id) %} +{% endif %} +{% endfor %} diff --git a/pillar.example b/pillar.example index 9f15300..05019aa 100644 --- a/pillar.example +++ b/pillar.example @@ -7,3 +7,90 @@ nginx: headers-more: source: http://github.com/agentzh/headers-more-nginx-module/tarball/v0.21 source_hash: sha1=dbf914cbf3f7b6cb7e033fa7b7c49e2f8879113b + +# ======== +# nginx.ng +# ======== + +nginx: + ng: + # These are usually set by grains in map.jinja + lookup: + package: nginx-custom + service: nginx + webuser: www-data + conf_file: /etc/nginx/nginx.conf + vhost_available: /etc/nginx/sites-available + vhost_enabled: /etc/nginx/sites-enabled + vhost_use_symlink: True + + # Source compilation is not currently a part of nginx.ng + from_source: False + + package: + opts: {} # this partially exposes parameters of pkg.installed + + service: + enable: True # Whether or not the service will be enabled/running or dead + opts: {} # this partially exposes parameters of service.running / service.dead + + server: + opts: {} # this partially exposes file.managed parameters as they relate to the main nginx.conf file + + # nginx.conf (main server) declarations + # dictionaries map to blocks {} and lists cause the same declaration to repeat with different values + config: + worker_processes: 4 + pid: /run/nginx.pid + events: + worker_connections: 768 + http: + sendfile: on + include: + - /etc/nginx/mime.types + - /etc/nginx/conf.d/*.conf + + vhosts: + disabled_postfix: .disabled # a postfix appended to files when doing non-symlink disabling + symlink_opts: {} # partially exposes file.symlink params when symlinking enabled sites + rename_opts: {} # partially exposes file.rename params when not symlinking disabled/enabled sites + managed_opts: {} # partially exposes file.managed params for managed vhost files + dir_opts: {} # partially exposes file.directory params for site available/enabled dirs + + # vhost declarations + # vhosts will default to being placed in vhost_available + managed: + mysite: # relative pathname of the vhost file + # may be True, False, or None where True is enabled, False, disabled, and None indicates no action + dir: /tmp # an alternate directory (not sites-available) where this vhost may be found + disabled_name: mysite.aint_on # an alternative disabled name to be use when not symlinking + enabled: True + + # May be a list of config options or None, if None, no vhost file will be managed/templated + # Take server directives as lists of dictionaries. If the dictionary value is another list of + # dictionaries a block {} will be started with the dictionary key name + config: + - server: + - server_name: localhost + - listen: + - 80 + - default_server + - index: + - index.html + - index.htm + - location ~ .htm: + - try_files: + - $uri + - $uri/ =404 + - test: something else + + # The above outputs: + # server { + # server_name localhost; + # listen 80 default_server; + # index index.html index.htm; + # location ~ .htm { + # try_files $uri $uri/ =404; + # test something else; + # } + # }