diff options
199 files changed, 8817 insertions, 1781 deletions
diff --git a/.tito/packages/openshift-ansible b/.tito/packages/openshift-ansible index 396f023ef..88c9754eb 100644 --- a/.tito/packages/openshift-ansible +++ b/.tito/packages/openshift-ansible @@ -1 +1 @@ -3.0.54-1 ./ +3.0.71-1 ./ diff --git a/.tito/releasers.conf b/.tito/releasers.conf index 36c15b109..ced23bd75 100644 --- a/.tito/releasers.conf +++ b/.tito/releasers.conf @@ -19,4 +19,4 @@ srpm_disttag = .el7aos [copr-openshift-ansible] releaser = tito.release.CoprReleaser -project_name = openshift-ansible +project_name = @OpenShiftOnlineOps/openshift-ansible diff --git a/README_libvirt.md b/README_libvirt.md index 8b46252b3..5c72eb64f 100644 --- a/README_libvirt.md +++ b/README_libvirt.md @@ -10,7 +10,7 @@ This makes `libvirt` useful to develop, test and debug OpenShift and openshift-a Install dependencies -------------------- -1. Install [ansible](http://www.ansible.com/) +1. Install [ansible](http://www.ansible.com/) 2. Install [dnsmasq](http://www.thekelleys.org.uk/dnsmasq/doc.html) 3. Install [ebtables](http://ebtables.netfilter.org/) 4. Install [qemu and qemu-system-x86](http://wiki.qemu.org/Main_Page) @@ -121,6 +121,11 @@ The following options can be passed via the `-o` flag of the `create` command or * `image_name` (default to `CentOS-7-x86_64-GenericCloud.qcow2`): Name of the QCOW2 image to boot the VMs on * `image_compression` (default to `xz`): Source QCOW2 compression (only xz supported at this time) * `image_sha256` (default to `dd0f5e610e7c5ffacaca35ed7a78a19142a588f4543da77b61c1fb0d74400471`): Expected SHA256 checksum of the downloaded image +* `libvirt_storage_pool` (default to `openshift-ansible`): name of the libvirt storage pool for the VM images. It will be created if it does not exist +* `libvirt_storage_pool_path` (default to `$HOME/libvirt-storage-pool-openshift-ansible`): path to `libvirt_storage_pool`, i.e. where the VM images are stored +* `libvirt_network` (default to `openshift-ansible`): name of the libvirt network that the VMs will use. It will be created if it does not exist +* `libvirt_instance_memory_mib` (default to `1024`): memory of the VMs in MiB +* `libvirt_instance_vcpu` (default to `2`): number of vCPUs of the VMs * `skip_image_download` (default to `no`): Skip QCOW2 image download. This requires the `image_name` QCOW2 image to be already present in `$HOME/libvirt-storage-pool-openshift-ansible` Creating a cluster diff --git a/bin/cluster b/bin/cluster index ecb8bc58e..fcab685ef 100755 --- a/bin/cluster +++ b/bin/cluster @@ -34,6 +34,8 @@ class Cluster(object): os.environ['ANSIBLE_HOST_KEY_CHECKING'] = 'False' # TODO: A more secure way to proceed would consist in dynamically # retrieving the ssh host public keys from the IaaS interface + if 'ANSIBLE_SSH_PIPELINING' not in os.environ: + os.environ['ANSIBLE_SSH_PIPELINING'] = 'True' def get_deployment_type(self, args): """ @@ -284,7 +286,20 @@ if __name__ == '__main__': cluster = Cluster() parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, description='Python wrapper to ensure proper configuration for OpenShift ansible playbooks', + epilog='''\ +This wrapper is overriding the following ansible variables: + + * ANSIBLE_SSH_ARGS: + If not set in the environment, this wrapper will use the following value: + `-o ForwardAgent=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ControlMaster=auto -o ControlPersist=600s` + If set in the environment, the environment variable value is left untouched and used. + + * ANSIBLE_SSH_PIPELINING: + If not set in the environment, this wrapper will set it to `True`. + If you experience issue with Ansible ssh pipelining, you can disable it by explicitely set this environment variable to `False`. +''' ) parser.add_argument('-v', '--verbose', action='count', help='Multiple -v options increase the verbosity') diff --git a/bin/openshift_ansible/awsutil.py b/bin/openshift_ansible/awsutil.py index eba11e851..11651f087 100644 --- a/bin/openshift_ansible/awsutil.py +++ b/bin/openshift_ansible/awsutil.py @@ -137,13 +137,13 @@ class AwsUtil(object): inst_by_env = {} for _, host in inv['_meta']['hostvars'].items(): # If you don't have an environment tag, we're going to ignore you - if 'ec2_tag_environment' not in host: + if 'oo_environment' not in host: continue - if host['ec2_tag_environment'] not in inst_by_env: - inst_by_env[host['ec2_tag_environment']] = {} - host_id = "%s:%s" % (host['ec2_tag_Name'], host['ec2_id']) - inst_by_env[host['ec2_tag_environment']][host_id] = host + if host['oo_environment'] not in inst_by_env: + inst_by_env[host['oo_environment']] = {} + host_id = "%s:%s" % (host['oo_name'], host['oo_id']) + inst_by_env[host['oo_environment']][host_id] = host return inst_by_env @@ -14,6 +14,8 @@ CONFIG_MAIN_SECTION = 'main' class Oscp(object): def __init__(self): + self.host = None + self.user = '' self.file_path = os.path.join(os.path.dirname(os.path.realpath(__file__))) # Default the config path to /etc @@ -56,8 +58,6 @@ class Oscp(object): def parse_cli_args(self): parser = argparse.ArgumentParser(description='OpenShift Online SSH Tool.') - parser.add_argument('-e', '--env', - action="store", help="Environment where this server exists.") parser.add_argument('-d', '--debug', default=False, action="store_true", help="debug mode") parser.add_argument('-v', '--verbose', default=False, @@ -82,8 +82,6 @@ class Oscp(object): def process_host(self): '''Determine host name and user name for SSH. ''' - self.user = '' - # is the first param passed a valid file? if os.path.isfile(self.args.src) or os.path.isdir(self.args.src): self.local_src = True @@ -108,76 +106,34 @@ class Oscp(object): self.host = search.groups()[0] self.path = search.groups()[1] - if self.args.env: - self.env = self.args.env - elif "." in self.host: - self.host, self.env = self.host.split(".") - else: - self.env = None - def get_hosts(self, refresh_cache=False): - '''Query our host inventory and return a dict where the format - equals: - - dict['environment'] = [{'servername' : {}}, ] - ''' + '''Query our host inventory and return a dict where the format ''' if refresh_cache: - self.host_inventory = self.aws.build_host_dict_by_env(['--refresh-cache']) + self.host_inventory = self.aws.get_inventory(['--refresh-cache'])['_meta']['hostvars'] else: - self.host_inventory = self.aws.build_host_dict_by_env() + self.host_inventory = self.aws.get_inventory()['_meta']['hostvars'] def select_host(self): '''select host attempts to match the host specified on the command line with a list of hosts. ''' - results = [] - for env in self.host_inventory.keys(): - for hostname, server_info in self.host_inventory[env].items(): - if hostname.split(':')[0] == self.host: - results.append((hostname, server_info)) - - # attempt to select the correct environment if specified - if self.env: - results = filter(lambda result: result[1]['oo_environment'] == self.env, results) - - if results: - return results + results = None + if self.host_inventory.has_key(self.host): + results = (self.host, self.host_inventory[self.host]) else: print "Could not find specified host: %s." % self.host # default - no results found. - return None + return results def list_hosts(self, limit=None): '''Function to print out the host inventory. Takes a single parameter to limit the number of hosts printed. ''' - - if self.env: - results = self.select_host() - if len(results) == 1: - hostname, server_info = results[0] - sorted_keys = server_info.keys() - sorted_keys.sort() - for key in sorted_keys: - print '{0:<35} {1}'.format(key, server_info[key]) - else: - for host_id, server_info in results[:limit]: - print '{oo_name:<35} {oo_clusterid:<10} {oo_environment:<8} ' \ - '{oo_id:<15} {oo_public_ip:<18} {oo_private_ip:<18}'.format(**server_info) - - if limit: - print - print 'Showing only the first %d results...' % limit - print - - else: - for env, host_ids in self.host_inventory.items(): - for host_id, server_info in host_ids.items(): - print '{oo_name:<35} {oo_clusterid:<10} {oo_environment:<8} ' \ - '{oo_id:<15} {oo_public_ip:<18} {oo_private_ip:<18}'.format(**server_info) - + for host_id, server_info in self.host_inventory.items(): + print '{oo_name:<35} {oo_clusterid:<10} {oo_environment:<8} ' \ + '{oo_id:<15} {oo_public_ip:<18} {oo_private_ip:<18}'.format(**server_info) def scp(self): '''scp files to or from a specified host @@ -203,17 +159,10 @@ class Oscp(object): if not results: return # early exit, no results - if len(results) > 1: - print "Multiple results found for %s." % self.host - for result in results: - print "{oo_name:<35} {oo_clusterid:<5} {oo_environment:<5} {oo_id:<10}".format(**result[1]) - return # early exit, too many results - # Assume we have one and only one. - hostname, server_info = results[0] - dns = server_info['oo_public_ip'] + server_info = results[1] - host_str = "%s%s%s" % (self.user, dns, self.path) + host_str = "%s%s%s" % (self.user, server_info['oo_public_ip'], self.path) if self.local_src: scp_args.append(self.args.src) @@ -14,6 +14,8 @@ CONFIG_MAIN_SECTION = 'main' class Ossh(object): def __init__(self): + self.user = None + self.host = None self.file_path = os.path.join(os.path.dirname(os.path.realpath(__file__))) # Default the config path to /etc @@ -54,8 +56,6 @@ class Ossh(object): def parse_cli_args(self): parser = argparse.ArgumentParser(description='OpenShift Online SSH Tool.') - parser.add_argument('-e', '--env', action="store", - help="Which environment to search for the host ") parser.add_argument('-d', '--debug', default=False, action="store_true", help="debug mode") parser.add_argument('-v', '--verbose', default=False, @@ -83,91 +83,48 @@ class Ossh(object): def process_host(self): '''Determine host name and user name for SSH. ''' - self.env = None - self.user = None - - re_env = re.compile("\.(" + "|".join(self.host_inventory.keys()) + ")") - search = re_env.search(self.args.host) - if self.args.env: - self.env = self.args.env - elif search: - # take the first? - self.env = search.groups()[0] - # remove env from hostname command line arg if found - if search: - self.args.host = re_env.split(self.args.host)[0] + parts = self.args.host.split('@') # parse username if passed - if '@' in self.args.host: - self.user, self.host = self.args.host.split('@') + if len(parts) > 1: + self.user = parts[0] + self.host = parts[1] else: - self.host = self.args.host + self.host = parts[0] + if self.args.login_name: self.user = self.args.login_name - def get_hosts(self, refresh_cache=False): - '''Query our host inventory and return a dict where the format - equals: - dict['servername'] = dns_name - ''' + def get_hosts(self, refresh_cache=False): + '''Query our host inventory and return a dict where the format ''' if refresh_cache: - self.host_inventory = self.aws.build_host_dict_by_env(['--refresh-cache']) + self.host_inventory = self.aws.get_inventory(['--refresh-cache'])['_meta']['hostvars'] else: - self.host_inventory = self.aws.build_host_dict_by_env() + self.host_inventory = self.aws.get_inventory()['_meta']['hostvars'] def select_host(self): '''select host attempts to match the host specified on the command line with a list of hosts. ''' - results = [] - for env in self.host_inventory.keys(): - for hostname, server_info in self.host_inventory[env].items(): - if hostname.split(':')[0] == self.host: - results.append((hostname, server_info)) - - # attempt to select the correct environment if specified - if self.env: - results = filter(lambda result: result[1]['oo_environment'] == self.env, results) - - if results: - return results + results = None + if self.host_inventory.has_key(self.host): + results = (self.host, self.host_inventory[self.host]) else: print "Could not find specified host: %s." % self.host # default - no results found. - return None + return results def list_hosts(self, limit=None): '''Function to print out the host inventory. Takes a single parameter to limit the number of hosts printed. ''' - - if self.env: - results = self.select_host() - if len(results) == 1: - hostname, server_info = results[0] - sorted_keys = server_info.keys() - sorted_keys.sort() - for key in sorted_keys: - print '{0:<35} {1}'.format(key, server_info[key]) - else: - for host_id, server_info in results[:limit]: - print '{oo_name:<35} {oo_clusterid:<10} {oo_environment:<8} ' \ - '{oo_id:<15} {oo_public_ip:<18} {oo_private_ip:<18}'.format(**server_info) - - if limit: - print - print 'Showing only the first %d results...' % limit - print - - else: - for env, host_ids in self.host_inventory.items(): - for host_id, server_info in host_ids.items(): - print '{oo_name:<35} {oo_clusterid:<10} {oo_environment:<8} ' \ - '{oo_id:<15} {oo_public_ip:<18} {oo_private_ip:<18}'.format(**server_info) + for host_id, server_info in self.host_inventory.items(): + print '{oo_name:<35} {oo_clusterid:<10} {oo_environment:<8} ' \ + '{oo_id:<15} {oo_public_ip:<18} {oo_private_ip:<18}'.format(**server_info) def ssh(self): '''SSH to a specified host @@ -193,17 +150,10 @@ class Ossh(object): if not results: return # early exit, no results - if len(results) > 1: - print "Multiple results found for %s." % self.host - for result in results: - print "{oo_name:<35} {oo_clusterid:<5} {oo_environment:<5} {oo_id:<10}".format(**result[1]) - return # early exit, too many results - # Assume we have one and only one. - _, server_info = results[0] - dns = server_info['oo_public_ip'] + server_info = results[1] - ssh_args.append(dns) + ssh_args.append(server_info['oo_public_ip']) #last argument if self.args.command: diff --git a/bin/ossh_bash_completion b/bin/ossh_bash_completion index 77b770a43..dcbde3e51 100755 --- a/bin/ossh_bash_completion +++ b/bin/ossh_bash_completion @@ -1,12 +1,12 @@ __ossh_known_hosts(){ if python -c 'import openshift_ansible' &>/dev/null; then - /usr/bin/python -c 'from openshift_ansible import multi_inventory; m=multi_inventory.MultiInventory(); m.run(); z=m.result; print "\n".join(["%s.%s" % (host["oo_name"],host["oo_environment"]) for dns, host in z["_meta"]["hostvars"].items() if all(k in host for k in ("oo_name", "oo_environment"))])' + /usr/bin/python -c 'from openshift_ansible import multi_inventory; m=multi_inventory.MultiInventory(); m.run(); z=m.result; print "\n".join([name for name in z["_meta"]["hostvars"].keys()])' elif [[ -f /dev/shm/.ansible/tmp/multi_inventory.cache ]]; then - /usr/bin/python -c 'import json; loc="/dev/shm/.ansible/tmp/multi_inventory.cache"; z=json.loads(open(loc).read()); print "\n".join(["%s.%s" % (host["oo_name"],host["oo_environment"]) for dns, host in z["_meta"]["hostvars"].items() if all(k in host for k in ("oo_name", "oo_environment"))])' + /usr/bin/python -c 'import json; loc="/dev/shm/.ansible/tmp/multi_inventory.cache"; z=json.loads(open(loc).read()); print "\n".join([name for name in z["_meta"]["hostvars"].keys()])' elif [[ -f ~/.ansible/tmp/multi_inventory.cache ]]; then - /usr/bin/python -c 'import json,os; loc="%s" % os.path.expanduser("~/.ansible/tmp/multi_inventory.cache"); z=json.loads(open(loc).read()); print "\n".join(["%s.%s" % (host["oo_name"],host["oo_environment"]) for dns, host in z["_meta"]["hostvars"].items() if all(k in host for k in ("oo_name", "oo_environment"))])' + /usr/bin/python -c 'import json,os; loc="%s" % os.path.expanduser("~/.ansible/tmp/multi_inventory.cache"); z=json.loads(open(loc).read()); print "\n".join([name for name in z["_meta"]["hostvars"].keys()])' fi } diff --git a/bin/ossh_zsh_completion b/bin/ossh_zsh_completion index 170ca889b..94ea61dab 100644 --- a/bin/ossh_zsh_completion +++ b/bin/ossh_zsh_completion @@ -2,13 +2,13 @@ _ossh_known_hosts(){ if python -c 'import openshift_ansible' &>/dev/null; then - print $(/usr/bin/python -c 'from openshift_ansible import multi_inventory; m=multi_inventory.MultiInventory(); m.run(); z=m.result; print "\n".join(["%s.%s" % (host["oo_name"],host["oo_environment"]) for dns, host in z["_meta"]["hostvars"].items() if all(k in host for k in ("oo_name", "oo_environment"))])') + print $(/usr/bin/python -c 'from openshift_ansible import multi_inventory; m=multi_inventory.MultiInventory(); m.run(); z=m.result; print "\n".join([name for name in z["_meta"]["hostvars"].keys()])') elif [[ -f /dev/shm/.ansible/tmp/multi_inventory.cache ]]; then - print $(/usr/bin/python -c 'import json; loc="/dev/shm/.ansible/tmp/multi_inventory.cache"; z=json.loads(open(loc).read()); print "\n".join(["%s.%s" % (host["oo_name"],host["oo_environment"]) for dns, host in z["_meta"]["hostvars"].items() if all(k in host for k in ("oo_name", "oo_environment"))])') + print $(/usr/bin/python -c 'import json; loc="/dev/shm/.ansible/tmp/multi_inventory.cache"; z=json.loads(open(loc).read()); print "\n".join([name for name in z["_meta"]["hostvars"].keys() ])') elif [[ -f ~/.ansible/tmp/multi_inventory.cache ]]; then - print $(/usr/bin/python -c 'import json,os; loc="%s" % os.path.expanduser("~/.ansible/tmp/multi_inventory.cache"); z=json.loads(open(loc).read()); print "\n".join(["%s.%s" % (host["oo_name"],host["oo_environment"]) for dns, host in z["_meta"]["hostvars"].items() if all(k in host for k in ("oo_name", "oo_environment"))])') + print $(/usr/bin/python -c 'import json,os; loc="%s" % os.path.expanduser("~/.ansible/tmp/multi_inventory.cache"); z=json.loads(open(loc).read()); print "\n".join([name for name in z["_meta"]["hostvars"].keys() ])') fi diff --git a/filter_plugins/oo_filters.py b/filter_plugins/oo_filters.py index 3dc3f2fe9..cd67b69a5 100644 --- a/filter_plugins/oo_filters.py +++ b/filter_plugins/oo_filters.py @@ -710,6 +710,22 @@ class FilterModule(object): return retval + @staticmethod + def oo_image_tag_to_rpm_version(version): + """ Convert an image tag string to an RPM version if necessary + Empty strings and strings that are already in rpm version format + are ignored. + + Ex. v3.2.0.10 -> -3.2.0.10 + """ + if not isinstance(version, basestring): + raise errors.AnsibleFilterError("|failed expects a string or unicode") + + if version.startswith("v"): + version = "-" + version.replace("v", "") + + return version + def filters(self): """ returns a mapping of filters to methods """ return { @@ -738,4 +754,5 @@ class FilterModule(object): "oo_31_rpm_rename_conversion": self.oo_31_rpm_rename_conversion, "oo_pods_match_component": self.oo_pods_match_component, "oo_get_hosts_from_hostvars": self.oo_get_hosts_from_hostvars, + "oo_image_tag_to_rpm_version": self.oo_image_tag_to_rpm_version, } diff --git a/filter_plugins/openshift_master.py b/filter_plugins/openshift_master.py index 3a1d77f53..d0fb98ec3 100644 --- a/filter_plugins/openshift_master.py +++ b/filter_plugins/openshift_master.py @@ -372,13 +372,12 @@ class OpenIDIdentityProvider(IdentityProviderOauthBase): raise errors.AnsibleFilterError("|failed claims for provider {0} " "must be a dictionary".format(self.__class__.__name__)) - if 'extraScopes' not in self.provider['extraScopes'] and not isinstance(self.provider['extraScopes'], list): - raise errors.AnsibleFilterError("|failed extraScopes for provider " - "{0} must be a list".format(self.__class__.__name__)) - if ('extraAuthorizeParameters' not in self.provider['extraAuthorizeParameters'] - and not isinstance(self.provider['extraAuthorizeParameters'], dict)): - raise errors.AnsibleFilterError("|failed extraAuthorizeParameters " - "for provider {0} must be a dictionary".format(self.__class__.__name__)) + for var, var_type in (('extraScopes', list), ('extraAuthorizeParameters', dict)): + if var in self.provider and not isinstance(self.provider[var], var_type): + raise errors.AnsibleFilterError("|failed {1} for provider " + "{0} must be a {2}".format(self.__class__.__name__, + var, + var_type.__class__.__name__)) required_claims = ['id'] optional_claims = ['email', 'name', 'preferredUsername'] diff --git a/inventory/byo/hosts.aep.example b/inventory/byo/hosts.aep.example index 637f13be6..43b646c93 100644 --- a/inventory/byo/hosts.aep.example +++ b/inventory/byo/hosts.aep.example @@ -62,18 +62,20 @@ deployment_type=atomic-enterprise # Add additional, insecure, and blocked registries to global docker configuration # For enterprise deployment types we ensure that registry.access.redhat.com is # included if you do not include it -#cli_docker_additional_registries=registry.example.com -#cli_docker_insecure_registries=registry.example.com -#cli_docker_blocked_registries=registry.hacker.com +#openshift_docker_additional_registries=registry.example.com +#openshift_docker_insecure_registries=registry.example.com +#openshift_docker_blocked_registries=registry.hacker.com +# Disable pushing to dockerhub +#openshift_docker_disable_push_dockerhub=True # Items added, as is, to end of /etc/sysconfig/docker OPTIONS #openshift_docker_options="-l warn --ipv6=false" # Deprecated methods to set --log-driver and --log-opts flags, use openshift_docker_options instead -#cli_docker_log_driver=json -#cli_docker_log_options="tag=mailer" +#openshift_docker_log_driver=json +#openshift_docker_log_options="tag=mailer" # Alternate image format string. If you're not modifying the format string and # only need to inject your own registry you may want to consider -# cli_docker_additional_registries instead +# openshift_docker_additional_registries instead #oreg_url=example.com/aep3/aep-${component}:${version} # Additional yum repos to install @@ -88,6 +90,30 @@ openshift_master_identity_providers=[{'name': 'htpasswd_auth', 'login': 'true', # LDAP auth #openshift_master_identity_providers=[{'name': 'my_ldap_provider', 'challenge': 'true', 'login': 'true', 'kind': 'LDAPPasswordIdentityProvider', 'attributes': {'id': ['dn'], 'email': ['mail'], 'name': ['cn'], 'preferredUsername': ['uid']}, 'bindDN': '', 'bindPassword': '', 'ca': '', 'insecure': 'false', 'url': 'ldap://ldap.example.com:389/ou=users,dc=example,dc=com?uid'}] +# Cloud Provider Configuration +# +# Note: You may make use of environment variables rather than store +# sensitive configuration within the ansible inventory. +# For example: +#openshift_cloudprovider_aws_access_key="{{ lookup('env','AWS_ACCESS_KEY_ID') }}" +#openshift_cloudprovider_aws_secret_key="{{ lookup('env','AWS_SECRET_ACCESS_KEY') }}" +# +# AWS +#openshift_cloudprovider_kind=aws +# Note: IAM profiles may be used instead of storing API credentials on disk. +#openshift_cloudprovider_aws_access_key=aws_access_key_id +#openshift_cloudprovider_aws_secret_key=aws_secret_access_key +# +# Openstack +#openshift_cloudprovider_kind=openstack +#openshift_cloudprovider_openstack_auth_url=http://openstack.example.com:35357/v2.0/ +#openshift_cloudprovider_openstack_username=username +#openshift_cloudprovider_openstack_password=password +#openshift_cloudprovider_openstack_tenand_id=tenant_id +#openshift_cloudprovider_openstack_tenant_name=tenant_name +#openshift_cloudprovider_openstack_region=region +#openshift_cloudprovider_openstack_lb_subnet_id=subnet_id + # Project Configuration #osm_project_request_message='' #osm_project_request_template='' diff --git a/inventory/byo/hosts.origin.example b/inventory/byo/hosts.origin.example index c30f65f9f..8b8dbade0 100644 --- a/inventory/byo/hosts.origin.example +++ b/inventory/byo/hosts.origin.example @@ -63,18 +63,20 @@ deployment_type=origin # Add additional, insecure, and blocked registries to global docker configuration # For enterprise deployment types we ensure that registry.access.redhat.com is # included if you do not include it -#cli_docker_additional_registries=registry.example.com -#cli_docker_insecure_registries=registry.example.com -#cli_docker_blocked_registries=registry.hacker.com +#openshift_docker_additional_registries=registry.example.com +#openshift_docker_insecure_registries=registry.example.com +#openshift_docker_blocked_registries=registry.hacker.com +# Disable pushing to dockerhub +#openshift_docker_disable_push_dockerhub=True # Items added, as is, to end of /etc/sysconfig/docker OPTIONS #openshift_docker_options="-l warn --ipv6=false" # Deprecated methods to set --log-driver and --log-opts flags, use openshift_docker_options instead -#cli_docker_log_driver=json -#cli_docker_log_options="tag=mailer" +#openshift_docker_log_driver=json +#openshift_docker_log_options="tag=mailer" # Alternate image format string. If you're not modifying the format string and # only need to inject your own registry you may want to consider -# cli_docker_additional_registries instead +# openshift_docker_additional_registries instead #oreg_url=example.com/openshift3/ose-${component}:${version} # Origin copr repo @@ -93,6 +95,30 @@ openshift_master_identity_providers=[{'name': 'htpasswd_auth', 'login': 'true', # LDAP auth #openshift_master_identity_providers=[{'name': 'my_ldap_provider', 'challenge': 'true', 'login': 'true', 'kind': 'LDAPPasswordIdentityProvider', 'attributes': {'id': ['dn'], 'email': ['mail'], 'name': ['cn'], 'preferredUsername': ['uid']}, 'bindDN': '', 'bindPassword': '', 'ca': '', 'insecure': 'false', 'url': 'ldap://ldap.example.com:389/ou=users,dc=example,dc=com?uid'}] +# Cloud Provider Configuration +# +# Note: You may make use of environment variables rather than store +# sensitive configuration within the ansible inventory. +# For example: +#openshift_cloudprovider_aws_access_key="{{ lookup('env','AWS_ACCESS_KEY_ID') }}" +#openshift_cloudprovider_aws_secret_key="{{ lookup('env','AWS_SECRET_ACCESS_KEY') }}" +# +# AWS +#openshift_cloudprovider_kind=aws +# Note: IAM profiles may be used instead of storing API credentials on disk. +#openshift_cloudprovider_aws_access_key=aws_access_key_id +#openshift_cloudprovider_aws_secret_key=aws_secret_access_key +# +# Openstack +#openshift_cloudprovider_kind=openstack +#openshift_cloudprovider_openstack_auth_url=http://openstack.example.com:35357/v2.0/ +#openshift_cloudprovider_openstack_username=username +#openshift_cloudprovider_openstack_password=password +#openshift_cloudprovider_openstack_tenand_id=tenant_id +#openshift_cloudprovider_openstack_tenant_name=tenant_name +#openshift_cloudprovider_openstack_region=region +#openshift_cloudprovider_openstack_lb_subnet_id=subnet_id + # Project Configuration #osm_project_request_message='' #osm_project_request_template='' diff --git a/inventory/byo/hosts.ose.example b/inventory/byo/hosts.ose.example index b51569e68..4c6aae0bd 100644 --- a/inventory/byo/hosts.ose.example +++ b/inventory/byo/hosts.ose.example @@ -62,19 +62,21 @@ deployment_type=openshift-enterprise # Add additional, insecure, and blocked registries to global docker configuration # For enterprise deployment types we ensure that registry.access.redhat.com is # included if you do not include it -#cli_docker_additional_registries=registry.example.com -#cli_docker_insecure_registries=registry.example.com -#cli_docker_blocked_registries=registry.hacker.com +#openshift_docker_additional_registries=registry.example.com +#openshift_docker_insecure_registries=registry.example.com +#openshift_docker_blocked_registries=registry.hacker.com +# Disable pushing to dockerhub +#openshift_docker_disable_push_dockerhub=True # Items added, as is, to end of /etc/sysconfig/docker OPTIONS #openshift_docker_options="-l warn --ipv6=false" # Deprecated methods to set --log-driver and --log-opts flags, use openshift_docker_options instead -#cli_docker_log_driver=json -#cli_docker_log_options="tag=mailer" +#openshift_docker_log_driver=json +#openshift_docker_log_options="tag=mailer" # Alternate image format string. If you're not modifying the format string and # only need to inject your own registry you may want to consider -# cli_docker_additional_registries instead +# openshift_docker_additional_registries instead #oreg_url=example.com/openshift3/ose-${component}:${version} # Additional yum repos to install @@ -89,6 +91,30 @@ openshift_master_identity_providers=[{'name': 'htpasswd_auth', 'login': 'true', # LDAP auth #openshift_master_identity_providers=[{'name': 'my_ldap_provider', 'challenge': 'true', 'login': 'true', 'kind': 'LDAPPasswordIdentityProvider', 'attributes': {'id': ['dn'], 'email': ['mail'], 'name': ['cn'], 'preferredUsername': ['uid']}, 'bindDN': '', 'bindPassword': '', 'ca': '', 'insecure': 'false', 'url': 'ldap://ldap.example.com:389/ou=users,dc=example,dc=com?uid'}] +# Cloud Provider Configuration +# +# Note: You may make use of environment variables rather than store +# sensitive configuration within the ansible inventory. +# For example: +#openshift_cloudprovider_aws_access_key="{{ lookup('env','AWS_ACCESS_KEY_ID') }}" +#openshift_cloudprovider_aws_secret_key="{{ lookup('env','AWS_SECRET_ACCESS_KEY') }}" +# +# AWS +#openshift_cloudprovider_kind=aws +# Note: IAM profiles may be used instead of storing API credentials on disk. +#openshift_cloudprovider_aws_access_key=aws_access_key_id +#openshift_cloudprovider_aws_secret_key=aws_secret_access_key +# +# Openstack +#openshift_cloudprovider_kind=openstack +#openshift_cloudprovider_openstack_auth_url=http://openstack.example.com:35357/v2.0/ +#openshift_cloudprovider_openstack_username=username +#openshift_cloudprovider_openstack_password=password +#openshift_cloudprovider_openstack_tenand_id=tenant_id +#openshift_cloudprovider_openstack_tenant_name=tenant_name +#openshift_cloudprovider_openstack_region=region +#openshift_cloudprovider_openstack_lb_subnet_id=subnet_id + # Project Configuration #osm_project_request_message='' #osm_project_request_template='' diff --git a/inventory/multi_inventory.py b/inventory/multi_inventory.py index 20fc48aa9..be597267e 100755 --- a/inventory/multi_inventory.py +++ b/inventory/multi_inventory.py @@ -25,6 +25,9 @@ class MultiInventoryException(Exception): '''Exceptions for MultiInventory class''' pass +# pylint: disable=too-many-public-methods +# After a refactor of too-many-branches and placing those branches into +# their own corresponding function, we have passed the allowed amount of functions(20). class MultiInventory(object): ''' MultiInventory class: @@ -282,35 +285,75 @@ class MultiInventory(object): else: return data.get(keys, None) - def apply_account_config(self, acc_config): - ''' Apply account config settings - ''' - results = self.all_inventory_results[acc_config['name']] - results['all_hosts'] = results['_meta']['hostvars'].keys() - + def apply_extra_vars(self, inventory, extra_vars): + ''' Apply the account config extra vars ''' # Extra vars go here - for new_var, value in acc_config.get('extra_vars', {}).items(): - for data in results['_meta']['hostvars'].values(): + for new_var, value in extra_vars.items(): + for data in inventory.values(): self.add_entry(data, new_var, value) + def apply_clone_vars(self, inventory, clone_vars): + ''' Apply the account config clone vars ''' # Clone vars go here - for to_name, from_name in acc_config.get('clone_vars', {}).items(): - for data in results['_meta']['hostvars'].values(): + for to_name, from_name in clone_vars.items(): + for data in inventory.values(): self.add_entry(data, to_name, self.get_entry(data, from_name)) - # Extra groups go here - for new_var, value in acc_config.get('extra_groups', {}).items(): - for data in results['_meta']['hostvars'].values(): - results["%s_%s" % (new_var, value)] = copy.copy(results['all_hosts']) - - # Clone groups go here - # Build a group based on the desired key name - for to_name, from_name in acc_config.get('clone_groups', {}).items(): - for name, data in results['_meta']['hostvars'].items(): + def apply_extra_groups(self, inventory, extra_groups): + ''' Apply the account config for extra groups ''' + _ = self # Here for pylint. wanted an instance method instead of static + for new_var, value in extra_groups.items(): + for _ in inventory['_meta']['hostvars'].values(): + inventory["%s_%s" % (new_var, value)] = copy.copy(inventory['all_hosts']) + + def apply_clone_groups(self, inventory, clone_groups): + ''' Apply the account config for clone groups ''' + for to_name, from_name in clone_groups.items(): + for name, data in inventory['_meta']['hostvars'].items(): key = '%s_%s' % (to_name, self.get_entry(data, from_name)) - if not results.has_key(key): - results[key] = [] - results[key].append(name) + if not inventory.has_key(key): + inventory[key] = [] + inventory[key].append(name) + + def apply_group_selectors(self, inventory, group_selectors): + ''' Apply the account config for group selectors ''' + _ = self # Here for pylint. wanted an instance method instead of static + # There could be multiple clusters per account. We need to process these selectors + # based upon the oo_clusterid_ variable. + clusterids = [group for group in inventory if "oo_clusterid_" in group] + + for clusterid in clusterids: + for selector in group_selectors: + if inventory.has_key(selector['from_group']): + hosts = list(set(inventory[clusterid]) & set(inventory[selector['from_group']])) + hosts.sort() + + # Multiple clusters in an account + if inventory.has_key(selector['name']): + inventory[selector['name']].extend(hosts[0:selector['count']]) + else: + inventory[selector['name']] = hosts[0:selector['count']] + + for host in hosts: + if host in inventory[selector['name']]: + inventory['_meta']['hostvars'][host][selector['name']] = True + else: + inventory['_meta']['hostvars'][host][selector['name']] = False + + def apply_account_config(self, acc_config): + ''' Apply account config settings ''' + results = self.all_inventory_results[acc_config['name']] + results['all_hosts'] = results['_meta']['hostvars'].keys() + + self.apply_extra_vars(results['_meta']['hostvars'], acc_config.get('extra_vars', {})) + + self.apply_clone_vars(results['_meta']['hostvars'], acc_config.get('clone_vars', {})) + + self.apply_extra_groups(results, acc_config.get('extra_groups', {})) + + self.apply_clone_groups(results, acc_config.get('clone_groups', {})) + + self.apply_group_selectors(results, acc_config.get('group_selectors', {})) # store the results back into all_inventory_results self.all_inventory_results[acc_config['name']] = results diff --git a/openshift-ansible.spec b/openshift-ansible.spec index bf2577d06..d66cf9d5c 100644 --- a/openshift-ansible.spec +++ b/openshift-ansible.spec @@ -5,7 +5,7 @@ } Name: openshift-ansible -Version: 3.0.54 +Version: 3.0.71 Release: 1%{?dist} Summary: Openshift and Atomic Enterprise Ansible License: ASL 2.0 @@ -279,6 +279,154 @@ Atomic OpenShift Utilities includes %changelog +* Mon Apr 04 2016 Troy Dawson <tdawson@redhat.com> 3.0.71-1 +- Fixed oc_edit by requiring name and content (kwoodson@redhat.com) +- add higher severity trigger if no heartbeat for 1 hour (jdiaz@redhat.com) +- Yedit enhancements (kwoodson@redhat.com) + +* Fri Apr 01 2016 Brenton Leanhardt <bleanhar@redhat.com> 3.0.70-1 +- Enable Ansible ssh pipelining to speedup deployment (lhuard@amadeus.com) +- Allow for overriding scheduler config (jdetiber@redhat.com) +- a-o-i: Add 3.2 to list of supported versions (smunilla@redhat.com) +- a-o-i: Support for unattended upgrades (smunilla@redhat.com) +- a-o-i: More flexible upgrade mappings (smunilla@redhat.com) +- a-o-i: OSE/AEP 3.2 product option (smunilla@redhat.com) +- a-o-i: Error out early if callback_facts is None (smunilla@redhat.com) + +* Thu Mar 31 2016 Brenton Leanhardt <bleanhar@redhat.com> 3.0.69-1 +- Bug 1320829 - Ensure docker installed for facts (jdetiber@redhat.com) +- Bug 1322788 - The IMAGE_VERSION wasn't added to atomic-openshift-master-api + and atomic-openshift-master-controllers (bleanhar@redhat.com) +- Fixed generate header. (kwoodson@redhat.com) +- Bug 1322335 - The package name is wrong for rpm upgrade (bleanhar@redhat.com) +- Add AWS cloud provider support. (abutcher@redhat.com) + +* Wed Mar 30 2016 Troy Dawson <tdawson@redhat.com> 3.0.68-1 +- Moving generation of ansible module side by side with module. + (kwoodson@redhat.com) +- Bug 1322338 - The upgrade should keep the option insecure- + registry=172.30.0.0/16 (bleanhar@redhat.com) + +* Tue Mar 29 2016 Troy Dawson <tdawson@redhat.com> 3.0.67-1 +- The systemd unit for atomic-openshift-master wasn't not being created + (bleanhar@redhat.com) +- Use openshift.master.ha instead of duplicating the logic + (bleanhar@redhat.com) +- Workaround for authenticated registries (bleanhar@redhat.com) +- First pass at systemd unit refactor (bleanhar@redhat.com) +- fix the key name for the dynamic item of avalable (zhizhang@zhizhang-laptop- + nay.redhat.com) +- make docker service want ose containerized services (sjenning@redhat.com) + +* Mon Mar 28 2016 Troy Dawson <tdawson@redhat.com> 3.0.66-1 +- Fixed error message to add valid yaml (kwoodson@redhat.com) +- added admin binary varibale usage as well as specifying kubeconfig copy to be + used (jkwiatko@redhat.com) +- Sync latest db-templates and qucikstart-templates (sdodson@redhat.com) +- adding playbook (jkwiatko@redhat.com) +- Tested of refactored code (jkwiatko@redhat.com) +- fix some typo (zhizhang@use-tower1.ops.rhcloud.com) +- add the total and available space item (zhizhang@use-tower1.ops.rhcloud.com) +- add dynamic pv count (zhizhang@use-tower1.ops.rhcloud.com) +- revised and restructured logging role (jkwiatko@redhat.com) +- Adding openshift_efk role (jkwiatko@redhat.com) +- Attempt to fix error validating when extraScopes and extraAuthorizeParameters + are not present (jdetiber@redhat.com) + +* Thu Mar 24 2016 Troy Dawson <tdawson@redhat.com> 3.0.65-1 +- Adding deployment config and refactored. (kwoodson@redhat.com) +- ManageIQ SA: Adding image-puller role (efreiber@redhat.com) + +* Wed Mar 23 2016 Troy Dawson <tdawson@redhat.com> 3.0.64-1 +- Latest cli updates from generated files (kwoodson@redhat.com) +- Add /dev to node containers (sdodson@redhat.com) +- Fix indention (whearn@redhat.com) +- Support setting local storage perFSGroup quota in node config. + (dgoodwin@redhat.com) +- Fix line break (whearn@redhat.com) +- Lock down permissions on named certificates (elyscape@gmail.com) +- Add namespace flag to oc create (whearn@redhat.com) + +* Mon Mar 21 2016 Kenny Woodson <kwoodson@redhat.com> 3.0.63-1 +- Modified group selectors for muliple clusters per account + (kwoodson@redhat.com) + +* Fri Mar 18 2016 Troy Dawson <tdawson@redhat.com> 3.0.62-1 +- Yaml editor first attempt (kwoodson@redhat.com) +- libvirt cluster variables cleanup (pep@redhat.com) + +* Thu Mar 17 2016 Troy Dawson <tdawson@redhat.com> 3.0.61-1 +- Bug 1317755 - Set insecure-registry for internal registry by default + (jdetiber@redhat.com) + +* Wed Mar 16 2016 Brenton Leanhardt <bleanhar@redhat.com> 3.0.60-1 +- Fall back to deployment_type in openshift_facts. (abutcher@redhat.com) +- Fixing undefined variable check (kwoodson@redhat.com) +- Fix path to cacert on /healthz/ready check (sdodson@redhat.com) +- Load environment files in containerized installs (sdodson@redhat.com) +- change type to value_type (zhizhang@zhizhang-laptop-nay.redhat.com) +- change time from int to float (zhizhang@zhizhang-laptop-nay.redhat.com) +- change the check time from 1 hour to 2 hour (zhizhang@zhizhang-laptop- + nay.redhat.com) +- add item of time cost a app build and app create (zhizhang@zhizhang-laptop- + nay.redhat.com) +- add trigger for app creation with build process (zhizhang@zhizhang-laptop- + nay.redhat.com) +- add key of openshift.master.app.build.create (zhizhang@zhizhang-laptop- + nay.redhat.com) + +* Wed Mar 16 2016 Brenton Leanhardt <bleanhar@redhat.com> 3.0.59-1 +- Only mask etcd service for containerized installls when it's installed + (sdodson@redhat.com) +- Provide cacert when performing health checks (abutcher@redhat.com) + +* Tue Mar 15 2016 Kenny Woodson <kwoodson@redhat.com> 3.0.58-1 +- Group selector feature added (kwoodson@redhat.com) +- nfs: replace yum with dnf (efreiber@redhat.com) +- Move common common facts to openshift_facts (jdetiber@redhat.com) +- perform oc client config tasks only once when ansible_ssh_user is root + (jdetiber@redhat.com) +- OSE/Origin < 3.2/1.2 should not get Docker 1.9 (sdodson@redhat.com) + +* Mon Mar 14 2016 Brenton Leanhardt <bleanhar@redhat.com> 3.0.57-1 +- Docker stderr can break this script if ansible executes it remotely + (bleanhar@redhat.com) +- Handle HA master case (bleanhar@redhat.com) +- Bug 1315564 - Containerized installs require a running environment + (bleanhar@redhat.com) +- Updating the docker registry variables to use the new name + (bleanhar@redhat.com) +- Bug 1316761 - Skip the available version check if openshift_image_tag is + defined. (bleanhar@redhat.com) +- Ansible module to manage secrets for openshift api (kwoodson@redhat.com) + +* Mon Mar 14 2016 Kenny Woodson <kwoodson@redhat.com> 3.0.56-1 +- Updating our metadata tooling to work without env (kwoodson@redhat.com) +- improve ordering of systemd units (jdetiber@redhat.com) +- Docker role refactor (jdetiber@redhat.com) +- Ensure is_containerized is cast as bool. (abutcher@redhat.com) +- Sync latest to v1.2 (sdodson@redhat.com) +- Sync with latest image stream and templates (sdodson@redhat.com) +- Allow origin version to be passed in as an argument (sdodson@redhat.com) +- Add support for Openstack integration (sbaubeau@redhat.com) +- Expose log level on the monitor (abhat@nuagenetworks.net) +- openshift_facts: Safe cast additional bools (smunilla@redhat.com) +- openshift-ansible: Wrap boolean facts (smunilla@redhat.com) +- fixed copr releasers file (twiest@redhat.com) +- Libvirt provider fixes (jdetiber@redhat.com) +- Support log level configuration for plugin (abhat@nuagenetworks.net) + +* Wed Mar 09 2016 Brenton Leanhardt <bleanhar@redhat.com> 3.0.55-1 +- Bug 1315564 - upgrade to ose3.2 failed on Atomic Hosts (bleanhar@redhat.com) +- Bug 1315563 - Upgrade failed to containerized install OSE 3.1 on RHEL + (bleanhar@redhat.com) +- a-o-i: Fix NFS storage tests (smunilla@redhat.com) +- First attempt at NFS setup (smunilla@redhat.com) +- reverting back to pre-pulling the master image (bleanhar@redhat.com) +- Use /healthz/ready when verifying api (abutcher@redhat.com) +- Formatting error (Viet.atx@gmail.com) +- Introduce origin-metrics playbook (vnguyen@redhat.com) + * Tue Mar 08 2016 Brenton Leanhardt <bleanhar@redhat.com> 3.0.54-1 - Bug 1315563 - stdout IO redirection wasn't working as expected over SSH connections (bleanhar@redhat.com) diff --git a/playbooks/adhoc/metrics_setup/README.md b/playbooks/adhoc/metrics_setup/README.md new file mode 100644 index 000000000..71aa1e109 --- /dev/null +++ b/playbooks/adhoc/metrics_setup/README.md @@ -0,0 +1,25 @@ +## Playbook for adding [Metrics](https://github.com/openshift/origin-metrics) to Openshift + +See OSE Ansible [readme](https://github.com/openshift/openshift-ansible/blob/master/README_OSE.md) for general install instructions. Playbook has been tested on OSE 3.1/RHEL7.2 cluster + + +Add the following vars to `[OSEv3:vars]` section of your inventory file +``` +[OSEv3:vars] +# Enable cluster metrics +use_cluster_metrics=true +metrics_external_service=< external service name for metrics > +metrics_image_prefix=rcm-img-docker01.build.eng.bos.redhat.com:5001/openshift3/ +metrics_image_version=3.1.0 +``` + +Run playbook +``` +ansible-playbook -i $INVENTORY_FILE playbooks/install.yml +``` + +## Contact +Email: hawkular-dev@lists.jboss.org + +## Credits +Playbook adapted from install shell scripts by Matt Mahoney diff --git a/playbooks/adhoc/metrics_setup/files/metrics-deployer-setup.yaml b/playbooks/adhoc/metrics_setup/files/metrics-deployer-setup.yaml new file mode 100644 index 000000000..f70e0b18b --- /dev/null +++ b/playbooks/adhoc/metrics_setup/files/metrics-deployer-setup.yaml @@ -0,0 +1,37 @@ +#!/bin/bash +# +# Copyright 2014-2015 Red Hat, Inc. and/or its affiliates +# and other contributors as indicated by the @author tags. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apiVersion: "v1" +kind: "List" +metadata: + name: metrics-deployer-setup + annotations: + description: "Required dependencies for the metrics deployer pod." + tags: "infrastructure" +labels: + metrics-infra: deployer + provider: openshift + component: deployer +items: +- + apiVersion: v1 + kind: ServiceAccount + metadata: + name: metrics-deployer + secrets: + - name: metrics-deployer diff --git a/playbooks/adhoc/metrics_setup/files/metrics.yaml b/playbooks/adhoc/metrics_setup/files/metrics.yaml new file mode 100644 index 000000000..d823b2587 --- /dev/null +++ b/playbooks/adhoc/metrics_setup/files/metrics.yaml @@ -0,0 +1,116 @@ +#!/bin/bash +# +# Copyright 2014-2015 Red Hat, Inc. and/or its affiliates +# and other contributors as indicated by the @author tags. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +apiVersion: "v1" +kind: "Template" +metadata: + name: metrics-deployer-template + annotations: + description: "Template for deploying the required Metrics integration. Requires cluster-admin 'metrics-deployer' service account and 'metrics-deployer' secret." + tags: "infrastructure" +labels: + metrics-infra: deployer + provider: openshift + component: deployer +objects: +- + apiVersion: v1 + kind: Pod + metadata: + generateName: metrics-deployer- + spec: + containers: + - image: ${IMAGE_PREFIX}metrics-deployer:${IMAGE_VERSION} + name: deployer + volumeMounts: + - name: secret + mountPath: /secret + readOnly: true + - name: empty + mountPath: /etc/deploy + env: + - name: PROJECT + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: IMAGE_PREFIX + value: ${IMAGE_PREFIX} + - name: IMAGE_VERSION + value: ${IMAGE_VERSION} + - name: PUBLIC_MASTER_URL + value: ${PUBLIC_MASTER_URL} + - name: MASTER_URL + value: ${MASTER_URL} + - name: REDEPLOY + value: ${REDEPLOY} + - name: USE_PERSISTENT_STORAGE + value: ${USE_PERSISTENT_STORAGE} + - name: HAWKULAR_METRICS_HOSTNAME + value: ${HAWKULAR_METRICS_HOSTNAME} + - name: CASSANDRA_NODES + value: ${CASSANDRA_NODES} + - name: CASSANDRA_PV_SIZE + value: ${CASSANDRA_PV_SIZE} + - name: METRIC_DURATION + value: ${METRIC_DURATION} + dnsPolicy: ClusterFirst + restartPolicy: Never + serviceAccount: metrics-deployer + volumes: + - name: empty + emptyDir: {} + - name: secret + secret: + secretName: metrics-deployer +parameters: +- + description: 'Specify prefix for metrics components; e.g. for "openshift/origin-metrics-deployer:v1.1", set prefix "openshift/origin-"' + name: IMAGE_PREFIX + value: "hawkular/" +- + description: 'Specify version for metrics components; e.g. for "openshift/origin-metrics-deployer:v1.1", set version "v1.1"' + name: IMAGE_VERSION + value: "0.7.0-SNAPSHOT" +- + description: "Internal URL for the master, for authentication retrieval" + name: MASTER_URL + value: "https://kubernetes.default.svc:443" +- + description: "External hostname where clients will reach Hawkular Metrics" + name: HAWKULAR_METRICS_HOSTNAME + required: true +- + description: "If set to true the deployer will try and delete all the existing components before trying to redeploy." + name: REDEPLOY + value: "false" +- + description: "Set to true for persistent storage, set to false to use non persistent storage" + name: USE_PERSISTENT_STORAGE + value: "true" +- + description: "The number of Cassandra Nodes to deploy for the initial cluster" + name: CASSANDRA_NODES + value: "1" +- + description: "The persistent volume size for each of the Cassandra nodes" + name: CASSANDRA_PV_SIZE + value: "1Gi" +- + description: "How many days metrics should be stored for." + name: METRIC_DURATION + value: "7" diff --git a/playbooks/adhoc/metrics_setup/playbooks/install.yml b/playbooks/adhoc/metrics_setup/playbooks/install.yml new file mode 100644 index 000000000..235f775ef --- /dev/null +++ b/playbooks/adhoc/metrics_setup/playbooks/install.yml @@ -0,0 +1,36 @@ +--- +- include: master_config_facts.yml +- name: "Install metrics" + hosts: masters + vars: + metrics_public_url: "https://{{ metrics_external_service }}/hawkular/metrics" + tasks: + - name: "Add metrics url to master config" + lineinfile: "state=present dest=/etc/origin/master/master-config.yaml regexp='^\ \ metricsPublicURL' insertbefore='^\ \ publicURL' line='\ \ metricsPublicURL: {{ metrics_public_url }}'" + + - name: "Restart master service" + service: name=atomic-openshift-master state=restarted + + - name: "Copy metrics-deployer yaml to remote" + copy: "src=../files/metrics-deployer-setup.yaml dest=/tmp/metrics-deployer-setup.yaml force=yes" + + - name: "Add metrics-deployer" + command: "{{item}}" + with_items: + - oc project openshift-infra + - oc create -f /tmp/metrics-deployer-setup.yaml + + - name: "Give metrics-deployer SA permissions" + command: "oadm policy add-role-to-user edit system:serviceaccount:openshift-infra:metrics-deployer" + + - name: "Give heapster SA permissions" + command: "oadm policy add-cluster-role-to-user cluster-reader system:serviceaccount:openshift-infra:heapster" + + - name: "Create metrics-deployer secret" + command: "oc secrets new metrics-deployer nothing=/dev/null" + + - name: "Copy metrics.yaml to remote" + copy: "src=../files/metrics.yaml dest=/tmp/metrics.yaml force=yes" + + - name: "Process yml template" + shell: "oc process -f /tmp/metrics.yaml -v MASTER_URL={{ masterPublicURL }},REDEPLOY=true,HAWKULAR_METRICS_HOSTNAME={{ metrics_external_service }},IMAGE_PREFIX={{ metrics_image_prefix }},IMAGE_VERSION={{ metrics_image_version }},USE_PERSISTENT_STORAGE=false | oc create -f -" diff --git a/playbooks/adhoc/metrics_setup/playbooks/master_config_facts.yml b/playbooks/adhoc/metrics_setup/playbooks/master_config_facts.yml new file mode 100644 index 000000000..65de11bc4 --- /dev/null +++ b/playbooks/adhoc/metrics_setup/playbooks/master_config_facts.yml @@ -0,0 +1,10 @@ +--- +- name: "Load master config" + hosts: masters + vars: + master_config_file: "/tmp/ansible-metrics-{{ ansible_hostname }}" + tasks: + - name: "Fetch master config from remote" + fetch: "src=/etc/origin/master/master-config.yaml dest={{ master_config_file }} flat=yes" + - name: "Load config" + include_vars: "{{ master_config_file }}" diff --git a/playbooks/adhoc/metrics_setup/playbooks/uninstall.yml b/playbooks/adhoc/metrics_setup/playbooks/uninstall.yml new file mode 100644 index 000000000..06c4586ee --- /dev/null +++ b/playbooks/adhoc/metrics_setup/playbooks/uninstall.yml @@ -0,0 +1,16 @@ +--- +- name: "Uninstall metrics" + hosts: masters + tasks: + - name: "Remove metrics url from master config" + lineinfile: "state=absent dest=/etc/origin/master/master-config.yaml regexp='^\ \ metricsPublicURL'" + + - name: "Delete metrics objects" + command: "{{item}}" + with_items: + - oc delete all --selector=metrics-infra + # - oc delete secrets --selector=metrics-infra + # - oc delete sa --selector=metrics-infra + - oc delete templates --selector=metrics-infra + - oc delete sa metrics-deployer + - oc delete secret metrics-deployer diff --git a/playbooks/adhoc/openshift_hosted_logging_efk.yaml b/playbooks/adhoc/openshift_hosted_logging_efk.yaml new file mode 100644 index 000000000..a3121d046 --- /dev/null +++ b/playbooks/adhoc/openshift_hosted_logging_efk.yaml @@ -0,0 +1,6 @@ +--- +- hosts: masters[0] + roles: + - role: openshift_hosted_logging + openshift_hosted_logging_cleanup: no + diff --git a/playbooks/byo/openshift-cluster/cluster_hosts.yml b/playbooks/byo/openshift-cluster/cluster_hosts.yml index 8893db245..658204c17 100644 --- a/playbooks/byo/openshift-cluster/cluster_hosts.yml +++ b/playbooks/byo/openshift-cluster/cluster_hosts.yml @@ -14,4 +14,6 @@ g_new_node_hosts: "{{ groups.new_nodes | default([]) }}" g_nfs_hosts: "{{ groups.nfs | default([]) }}" g_all_hosts: "{{ g_master_hosts | union(g_node_hosts) | union(g_etcd_hosts) - | union(g_lb_hosts) | default([]) }}" + | union(g_lb_hosts) | union(g_nfs_hosts) + | union(g_new_node_hosts)| union(g_new_master_hosts) + | default([]) }}" diff --git a/playbooks/common/openshift-cluster/config.yml b/playbooks/common/openshift-cluster/config.yml index 23c8f039e..2411e7360 100644 --- a/playbooks/common/openshift-cluster/config.yml +++ b/playbooks/common/openshift-cluster/config.yml @@ -3,7 +3,27 @@ - include: validate_hostnames.yml -- include: ../openshift-docker/config.yml +- name: Set oo_options + hosts: oo_hosts_to_config + tasks: + - set_fact: + openshift_docker_additional_registries: "{{ lookup('oo_option', 'docker_additional_registries') }}" + when: openshift_docker_additional_registries is not defined + - set_fact: + openshift_docker_insecure_registries: "{{ lookup('oo_option', 'docker_insecure_registries') }}" + when: openshift_docker_insecure_registries is not defined + - set_fact: + openshift_docker_blocked_registries: "{{ lookup('oo_option', 'docker_blocked_registries') }}" + when: openshift_docker_blocked_registries is not defined + - set_fact: + openshift_docker_options: "{{ lookup('oo_option', 'docker_options') }}" + when: openshift_docker_options is not defined + - set_fact: + openshift_docker_log_driver: "{{ lookup('oo_option', 'docker_log_driver') }}" + when: openshift_docker_log_driver is not defined + - set_fact: + openshift_docker_log_options: "{{ lookup('oo_option', 'docker_log_options') }}" + when: openshift_docker_log_options is not defined - include: ../openshift-etcd/config.yml diff --git a/playbooks/common/openshift-cluster/evaluate_groups.yml b/playbooks/common/openshift-cluster/evaluate_groups.yml index 432a92b49..ce0134c44 100644 --- a/playbooks/common/openshift-cluster/evaluate_groups.yml +++ b/playbooks/common/openshift-cluster/evaluate_groups.yml @@ -29,6 +29,14 @@ msg: The nfs group must be limited to one host when: (groups[g_nfs_hosts] | default([])) | length > 1 + - name: Evaluate oo_all_hosts + add_host: + name: "{{ item }}" + groups: oo_all_hosts + ansible_ssh_user: "{{ g_ssh_user | default(omit) }}" + ansible_sudo: "{{ g_sudo | default(omit) }}" + with_items: "{{ g_all_hosts | default([]) }}" + - name: Evaluate oo_masters add_host: name: "{{ item }}" diff --git a/playbooks/common/openshift-cluster/upgrades/files/ensure_system_units_have_version.sh b/playbooks/common/openshift-cluster/upgrades/files/ensure_system_units_have_version.sh deleted file mode 100644 index 239f43314..000000000 --- a/playbooks/common/openshift-cluster/upgrades/files/ensure_system_units_have_version.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash -set -e - -SERVICE_TYPE=$1 -DEPLOYMENT_TYPE=$2 -VERSION="v${3}" - -add_image_version_to_sysconfig () { - unit_name=$2 - sysconfig_file=/etc/sysconfig/${unit_name} - - if ! grep IMAGE_VERSION ${sysconfig_file}; then - sed -i "/CONFIG_FILE/a IMAGE_VERSION=${1}" ${sysconfig_file} - else - sed -i "s/\(IMAGE_VERSION=\).*/\1${1}/" ${sysconfig_file} - fi -} - -add_image_version_to_unit () { - deployment_type=$1 - unit_file=$2 - - if ! grep IMAGE_VERSION $unit_file; then - image_namespace="openshift/" - if [ $deployment_type == "atomic-enterprise" ]; then - image_namespace="aep3/" - elif [ $deployment_type == "openshift-enterprise" ]; then - image_namespace="openshift3/" - fi - - sed -i "s|\(${image_namespace}[a-zA-Z0-9]\+\)|\1:\${IMAGE_VERSION}|" $unit_file - fi -} - -for unit_file in $(ls /etc/systemd/system/${SERVICE_TYPE}*.service); do - unit_name=$(basename -s .service ${unit_file}) - add_image_version_to_sysconfig $VERSION $unit_name - add_image_version_to_unit $DEPLOYMENT_TYPE $unit_file -done - -if [ -e /etc/sysconfig/openvswitch ]; then - add_image_version_to_sysconfig $VERSION openvswitch -else - echo IMAGE_VERSION=${VERSION} > /etc/sysconfig/openvswitch -fi -if ! grep EnvironmentFile /etc/systemd/system/openvswitch.service > /dev/null; then - sed -i "/Service/a EnvironmentFile=/etc/sysconfig/openvswitch" /etc/systemd/system/openvswitch.service -fi -add_image_version_to_unit $DEPLOYMENT_TYPE /etc/systemd/system/openvswitch.service - -systemctl daemon-reload diff --git a/playbooks/common/openshift-cluster/upgrades/files/openshift_container_versions.sh b/playbooks/common/openshift-cluster/upgrades/files/openshift_container_versions.sh index ba17ca671..7a1edf38f 100644 --- a/playbooks/common/openshift-cluster/upgrades/files/openshift_container_versions.sh +++ b/playbooks/common/openshift-cluster/upgrades/files/openshift_container_versions.sh @@ -4,18 +4,18 @@ # We just need to know the version of one of them. unit_file=$(ls /etc/systemd/system/${1}*.service | head -n1) installed_container_name=$(basename -s .service ${unit_file}) -installed=$(docker exec ${installed_container_name} openshift version | grep openshift | awk '{ print $2 }' | cut -f1 -d"-" | tr -d 'v') +installed=$(docker exec ${installed_container_name} openshift version 2> /dev/null | grep openshift | awk '{ print $2 }' | cut -f1 -d"-" | tr -d 'v') if [ ${1} == "origin" ]; then image_name="openshift/origin" elif grep aep $unit_file 2>&1 > /dev/null; then image_name="aep3/aep" -elif grep ose $unit_file 2>&1 > /dev/null; then +elif grep openshift3 $unit_file 2>&1 > /dev/null; then image_name="openshift3/ose" fi docker pull ${image_name} 2>&1 > /dev/null -available=$(docker run --rm ${image_name} version | grep openshift | awk '{ print $2 }' | cut -f1 -d"-" | tr -d 'v') +available=$(docker run --rm ${image_name} version 2> /dev/null | grep openshift | awk '{ print $2 }' | cut -f1 -d"-" | tr -d 'v') echo "---" echo "curr_version: ${installed}" diff --git a/playbooks/common/openshift-cluster/upgrades/v3_0_to_v3_1/upgrade.yml b/playbooks/common/openshift-cluster/upgrades/v3_0_to_v3_1/upgrade.yml index 6d7cefc8e..31ba8c4a9 100644 --- a/playbooks/common/openshift-cluster/upgrades/v3_0_to_v3_1/upgrade.yml +++ b/playbooks/common/openshift-cluster/upgrades/v3_0_to_v3_1/upgrade.yml @@ -212,13 +212,10 @@ - name: Update deployment type hosts: oo_masters_to_config:oo_nodes_to_config:oo_etcd_to_config + vars: + openshift_deployment_type: "{{ deployment_type }}" roles: - openshift_facts - post_tasks: - - openshift_facts: - role: common - local_facts: - deployment_type: "{{ deployment_type }}" - name: Update master facts hosts: oo_masters_to_config diff --git a/playbooks/common/openshift-cluster/upgrades/v3_1_to_v3_2/containerized_upgrade.yml b/playbooks/common/openshift-cluster/upgrades/v3_1_to_v3_2/containerized_upgrade.yml index 696994688..cc587bfa1 100644 --- a/playbooks/common/openshift-cluster/upgrades/v3_1_to_v3_2/containerized_upgrade.yml +++ b/playbooks/common/openshift-cluster/upgrades/v3_1_to_v3_2/containerized_upgrade.yml @@ -1,8 +1,10 @@ -- name: Update system_units - script: ../files/ensure_system_units_have_version.sh {{ openshift.common.service_type }} {{ openshift.common.deployment_type }} {{ g_new_version }} +- include_vars: ../../../../../roles/openshift_node/vars/main.yml + +- name: Update systemd units + include: ../../../../../roles/openshift_node/tasks/systemd_units.yml openshift_version=g_aos_versions.avail_version - name: Verifying the correct version was configured - command: grep {{ verify_upgrade_version }} {{ item }} + shell: grep {{ verify_upgrade_version }} {{ item }} with_items: - /etc/sysconfig/openvswitch - /etc/sysconfig/{{ openshift.common.service_type }}* diff --git a/playbooks/common/openshift-cluster/upgrades/v3_1_to_v3_2/docker_upgrade.yml b/playbooks/common/openshift-cluster/upgrades/v3_1_to_v3_2/docker_upgrade.yml index d384631d3..d9177e8a0 100644 --- a/playbooks/common/openshift-cluster/upgrades/v3_1_to_v3_2/docker_upgrade.yml +++ b/playbooks/common/openshift-cluster/upgrades/v3_1_to_v3_2/docker_upgrade.yml @@ -1,20 +1,14 @@ ---- -- name: Upgrade Docker - hosts: oo_masters_to_config:oo_nodes_to_config:oo_etcd_to_config - vars: - openshift_version: "{{ openshift_pkg_version | default('') }}" - tasks: - - name: Check if Docker is installed - command: rpm -q docker - register: pkg_check - failed_when: pkg_check.rc > 1 - changed_when: no +- name: Check if Docker is installed + command: rpm -q docker + register: pkg_check + failed_when: pkg_check.rc > 1 + changed_when: no - - name: Upgrade Docker - command: "{{ ansible_pkg_mgr}} update -y docker" - when: pkg_check.rc == 0 and g_docker_version.curr_version | version_compare('1.9','<') - register: docker_upgrade +- name: Upgrade Docker + command: "{{ ansible_pkg_mgr}} update -y docker" + when: pkg_check.rc == 0 and g_docker_version.curr_version | version_compare('1.9','<') + register: docker_upgrade - - name: Restart Docker - service: name=docker state=restarted - when: docker_upgrade | changed +- name: Restart Docker + service: name=docker state=restarted + when: docker_upgrade | changed diff --git a/playbooks/common/openshift-cluster/upgrades/v3_1_to_v3_2/pre.yml b/playbooks/common/openshift-cluster/upgrades/v3_1_to_v3_2/pre.yml index 68cbf35c0..abc0d81c7 100644 --- a/playbooks/common/openshift-cluster/upgrades/v3_1_to_v3_2/pre.yml +++ b/playbooks/common/openshift-cluster/upgrades/v3_1_to_v3_2/pre.yml @@ -24,7 +24,6 @@ - name: Verify upgrade can proceed hosts: oo_first_master vars: - openshift_master_ha: "{{ groups.oo_masters_to_config | length > 1 }}" target_version: "{{ '1.2' if deployment_type == 'origin' else '3.1.1.900' }}" gather_facts: no tasks: @@ -47,9 +46,57 @@ when: openshift_image_tag is defined and openshift_image_tag.split('v',1).1 | version_compare(target_version ,'<') - name: Verify upgrade can proceed + hosts: oo_masters_to_config + roles: + - openshift_facts + tasks: + - name: Ensure Master is running + service: + name: "{{ openshift.common.service_type }}-master" + state: started + enabled: yes + when: openshift.master.ha is defined and not openshift.master.ha | bool and openshift.common.is_containerized | bool + + - name: Ensure HA Master is running + service: + name: "{{ openshift.common.service_type }}-master-api" + state: started + enabled: yes + when: openshift.master.ha is defined and openshift.master.ha | bool and openshift.common.is_containerized | bool + + - name: Ensure HA Master is running + service: + name: "{{ openshift.common.service_type }}-master-controllers" + state: started + enabled: yes + when: openshift.master.ha is defined and openshift.master.ha | bool and openshift.common.is_containerized | bool + post_tasks: + - openshift_facts: + role: master + local_facts: + ha: "{{ groups.oo_masters_to_config | length > 1 }}" + +- name: Verify upgrade can proceed + hosts: oo_nodes_to_config + roles: + - openshift_facts + tasks: + - name: Ensure Node is running + service: + name: "{{ openshift.common.service_type }}-node" + state: started + enabled: yes + when: openshift.common.is_containerized | bool + +- name: Verify upgrade can proceed hosts: oo_masters_to_config:oo_nodes_to_config vars: target_version: "{{ '1.2' if deployment_type == 'origin' else '3.1.1.900' }}" + openshift_docker_hosted_registry_insecure: True + openshift_docker_hosted_registry_network: "{{ hostvars[groups.oo_first_master.0].openshift.master.portal_net }}" + handlers: + - include: ../../../../../roles/openshift_master/handlers/main.yml + - include: ../../../../../roles/openshift_node/handlers/main.yml roles: - openshift_cli tasks: @@ -95,9 +142,19 @@ msg: Verifying the correct version was found when: verify_upgrade_version is defined and g_new_version != verify_upgrade_version + - include_vars: ../../../../../roles/openshift_master/vars/main.yml + when: inventory_hostname in groups.oo_masters_to_config + - name: Update systemd units - script: ../files/ensure_system_units_have_version.sh {{ openshift.common.service_type }} {{ openshift.common.deployment_type }} {{ g_aos_versions.curr_version }} - when: openshift.common.is_containerized | bool + include: ../../../../../roles/openshift_master/tasks/systemd_units.yml openshift_version=g_aos_versions.curr_version + when: inventory_hostname in groups.oo_masters_to_config + + - include_vars: ../../../../../roles/openshift_node/vars/main.yml + when: inventory_hostname in groups.oo_nodes_to_config + + - name: Update systemd units + include: ../../../../../roles/openshift_node/tasks/systemd_units.yml openshift_version=g_aos_versions.curr_version + when: inventory_hostname in groups.oo_nodes_to_config # Note: the version number is hardcoded here in hopes of catching potential # bugs in how g_aos_versions.curr_version is set @@ -108,6 +165,13 @@ - /etc/sysconfig/{{ openshift.common.service_type }}* when: verify_upgrade_version is defined + - name: Verifying the image version is used in the systemd unit + shell: grep IMAGE_VERSION {{ item }} + with_items: + - /etc/systemd/system/openvswitch.service + - /etc/systemd/system/{{ openshift.common.service_type }}*.service + when: openshift.common.is_containerized | bool + - fail: msg: This playbook requires Origin 1.1 or later when: deployment_type == 'origin' and g_aos_versions.curr_version | version_compare('1.1','<') @@ -116,10 +180,9 @@ msg: This playbook requires Atomic Enterprise Platform/OpenShift Enterprise 3.1 or later when: deployment_type == 'atomic-openshift' and g_aos_versions.curr_version | version_compare('3.1','<') - # TODO: this may only make sense for RPM installs. We probably need another check for containerized installs. - fail: msg: Upgrade packages not found - when: (g_aos_versions.avail_version | default(g_aos_versions.curr_version, true) | version_compare(target_version, '<')) + when: openshift_image_tag is not defined and (g_aos_versions.avail_version | default(g_aos_versions.curr_version, true) | version_compare(target_version, '<')) - name: Determine available Docker script: ../files/rpm_versions.sh docker diff --git a/playbooks/common/openshift-cluster/upgrades/v3_1_to_v3_2/upgrade.yml b/playbooks/common/openshift-cluster/upgrades/v3_1_to_v3_2/upgrade.yml index 3552b8734..b393b64fe 100644 --- a/playbooks/common/openshift-cluster/upgrades/v3_1_to_v3_2/upgrade.yml +++ b/playbooks/common/openshift-cluster/upgrades/v3_1_to_v3_2/upgrade.yml @@ -1,22 +1,62 @@ --- +# This is a workaround for authenticated registries +- name: Download new images + hosts: oo_nodes_to_config + roles: + - openshift_facts + tasks: + - name: Pull Images + command: > + docker pull {{ item }}:v{{ g_new_version }} + with_items: + - "{{ openshift.node.node_image }}" + - "{{ openshift.node.ovs_image }}" + - "{{ openshift.common.pod_image }}" + - "{{ openshift.common.router_image }}" + - "{{ openshift.common.registry_image }}" + - "{{ openshift.common.deployer_image }}" + +# This is a workaround for authenticated registries +- name: Download new images + hosts: oo_masters_to_config + roles: + - openshift_facts + tasks: + - name: Pull Images + command: > + docker pull {{ item }}:v{{ g_new_version }} + with_items: + - "{{ openshift.master.master_image }}" + ############################################################################### # The restart playbook should be run after this playbook completes. ############################################################################### -- include: docker_upgrade.yml - when: not openshift.common.is_atomic | bool +- name: Upgrade docker + hosts: oo_masters_to_config:oo_nodes_to_config:oo_etcd_to_config + roles: + - openshift_facts + tasks: + - include: docker_upgrade.yml + when: not openshift.common.is_atomic | bool ############################################################################### # Upgrade Masters ############################################################################### - name: Upgrade master hosts: oo_masters_to_config + handlers: + - include: ../../../../../roles/openshift_master/handlers/main.yml + roles: + - openshift_facts tasks: - include: rpm_upgrade.yml component=master when: not openshift.common.is_containerized | bool - - include: containerized_upgrade.yml - when: openshift.common.is_containerized | bool + - include_vars: ../../../../../roles/openshift_master/vars/main.yml + + - name: Update systemd units + include: ../../../../../roles/openshift_master/tasks/systemd_units.yml openshift_version=g_aos_versions.avail_version # - name: Upgrade master configuration # openshift_upgrade_config: @@ -54,6 +94,10 @@ ############################################################################### - name: Upgrade nodes hosts: oo_nodes_to_config + roles: + - openshift_facts + handlers: + - include: ../../../../../roles/openshift_node/handlers/main.yml tasks: - include: rpm_upgrade.yml vars: @@ -99,7 +143,8 @@ vars: origin_reconcile_bindings: "{{ deployment_type == 'origin' and g_new_version | version_compare('1.0.6', '>') }}" ent_reconcile_bindings: true - openshift_master_ha: "{{ groups.oo_masters_to_config | length > 1 }}" + openshift_docker_hosted_registry_insecure: True + openshift_docker_hosted_registry_network: "{{ hostvars[groups.oo_first_master.0].openshift.master.portal_net }}" tasks: - name: Verifying the correct commandline tools are available shell: grep {{ verify_upgrade_version }} {{ openshift.common.admin_binary}} diff --git a/playbooks/common/openshift-cluster/validate_hostnames.yml b/playbooks/common/openshift-cluster/validate_hostnames.yml index fd82997b9..0f562e019 100644 --- a/playbooks/common/openshift-cluster/validate_hostnames.yml +++ b/playbooks/common/openshift-cluster/validate_hostnames.yml @@ -6,14 +6,6 @@ roles: - openshift_facts tasks: - - openshift_facts: - role: "{{ item.role }}" - local_facts: "{{ item.local_facts }}" - with_items: - - role: common - local_facts: - hostname: "{{ openshift_hostname | default(None) }}" - public_hostname: "{{ openshift_public_hostname | default(None) }}" - shell: getent ahostsv4 {{ openshift.common.hostname }} | head -n 1 | awk '{ print $1 }' register: lookupip diff --git a/playbooks/common/openshift-docker/config.yml b/playbooks/common/openshift-docker/config.yml deleted file mode 100644 index 092d5533c..000000000 --- a/playbooks/common/openshift-docker/config.yml +++ /dev/null @@ -1,9 +0,0 @@ -- name: Configure docker hosts - hosts: oo_masters_to_config:oo_nodes_to_config:oo_etcd_to_config - vars: - docker_additional_registries: "{{ lookup('oo_option', 'docker_additional_registries') | oo_split }}" - docker_insecure_registries: "{{ lookup('oo_option', 'docker_insecure_registries') | oo_split }}" - docker_blocked_registries: "{{ lookup('oo_option', 'docker_blocked_registries') | oo_split }}" - roles: - - openshift_facts - - openshift_docker diff --git a/playbooks/common/openshift-docker/filter_plugins b/playbooks/common/openshift-docker/filter_plugins deleted file mode 120000 index 99a95e4ca..000000000 --- a/playbooks/common/openshift-docker/filter_plugins +++ /dev/null @@ -1 +0,0 @@ -../../../filter_plugins
\ No newline at end of file diff --git a/playbooks/common/openshift-docker/lookup_plugins b/playbooks/common/openshift-docker/lookup_plugins deleted file mode 120000 index ac79701db..000000000 --- a/playbooks/common/openshift-docker/lookup_plugins +++ /dev/null @@ -1 +0,0 @@ -../../../lookup_plugins
\ No newline at end of file diff --git a/playbooks/common/openshift-docker/roles b/playbooks/common/openshift-docker/roles deleted file mode 120000 index 20c4c58cf..000000000 --- a/playbooks/common/openshift-docker/roles +++ /dev/null @@ -1 +0,0 @@ -../../../roles
\ No newline at end of file diff --git a/playbooks/common/openshift-etcd/config.yml b/playbooks/common/openshift-etcd/config.yml index 93eb157cb..2f07b2f51 100644 --- a/playbooks/common/openshift-etcd/config.yml +++ b/playbooks/common/openshift-etcd/config.yml @@ -5,17 +5,9 @@ - openshift_facts tasks: - openshift_facts: - role: "{{ item.role }}" - local_facts: "{{ item.local_facts }}" - with_items: - - role: common - local_facts: - hostname: "{{ openshift_hostname | default(None) }}" - public_hostname: "{{ openshift_public_hostname | default(None) }}" - deployment_type: "{{ openshift_deployment_type }}" - - role: etcd - local_facts: - etcd_image: "{{ osm_etcd_image | default(None) }}" + role: etcd + local_facts: + etcd_image: "{{ osm_etcd_image | default(None) }}" - name: Check status of etcd certificates stat: path: "{{ item }}" @@ -89,8 +81,8 @@ dest: "{{ etcd_cert_config_dir }}" when: etcd_server_certs_missing roles: - - etcd - - role: nickhammond.logrotate + - openshift_etcd + - nickhammond.logrotate # Configure the remaining etcd hosts, skipping the first one we dealt with above. - name: Configure remaining etcd hosts @@ -111,7 +103,7 @@ dest: "{{ etcd_cert_config_dir }}" when: etcd_server_certs_missing roles: - - etcd + - openshift_etcd - role: nickhammond.logrotate - name: Delete temporary directory on localhost diff --git a/playbooks/common/openshift-master/config.yml b/playbooks/common/openshift-master/config.yml index 3c7d94c96..f1eaf8e16 100644 --- a/playbooks/common/openshift-master/config.yml +++ b/playbooks/common/openshift-master/config.yml @@ -1,6 +1,9 @@ --- - name: Set master facts and determine if external etcd certs need to be generated hosts: oo_masters_to_config + vars: + t_oo_option_master_debug_level: "{{ lookup('oo_option', 'openshift_master_debug_level') }}" + pre_tasks: - name: Check for RPM generated config marker file .config_managed stat: @@ -30,40 +33,29 @@ | default(none, true) }}" - set_fact: - openshift_master_debug_level: "{{ lookup('oo_option', 'openshift_master_debug_level') | default(openshift.common.debug_level, true) }}" - when: openshift_master_debug_level is not defined - + openshift_master_debug_level: "{{ t_oo_option_master_debug_level }}" + when: openshift_master_debug_level is not defined and t_oo_option_master_debug_level != "" roles: - openshift_facts post_tasks: - openshift_facts: - role: "{{ item.role }}" - local_facts: "{{ item.local_facts }}" - with_items: - - role: common - local_facts: - hostname: "{{ openshift_hostname | default(None) }}" - ip: "{{ openshift_ip | default(None) }}" - public_hostname: "{{ openshift_public_hostname | default(None) }}" - public_ip: "{{ openshift_public_ip | default(None) }}" - deployment_type: "{{ openshift_deployment_type }}" - - role: master - local_facts: - api_port: "{{ openshift_master_api_port | default(None) }}" - api_url: "{{ openshift_master_api_url | default(None) }}" - api_use_ssl: "{{ openshift_master_api_use_ssl | default(None) }}" - controllers_port: "{{ openshift_master_controllers_port | default(None) }}" - public_api_url: "{{ openshift_master_public_api_url | default(None) }}" - cluster_hostname: "{{ openshift_master_cluster_hostname | default(None) }}" - cluster_public_hostname: "{{ openshift_master_cluster_public_hostname | default(None) }}" - console_path: "{{ openshift_master_console_path | default(None) }}" - console_port: "{{ openshift_master_console_port | default(None) }}" - console_url: "{{ openshift_master_console_url | default(None) }}" - console_use_ssl: "{{ openshift_master_console_use_ssl | default(None) }}" - public_console_url: "{{ openshift_master_public_console_url | default(None) }}" - portal_net: "{{ openshift_master_portal_net | default(None) }}" - ha: "{{ openshift_master_ha | default(groups.oo_masters | length > 1) }}" - master_count: "{{ openshift_master_count | default(groups.oo_masters | length) }}" + role: master + local_facts: + api_port: "{{ openshift_master_api_port | default(None) }}" + api_url: "{{ openshift_master_api_url | default(None) }}" + api_use_ssl: "{{ openshift_master_api_use_ssl | default(None) }}" + controllers_port: "{{ openshift_master_controllers_port | default(None) }}" + public_api_url: "{{ openshift_master_public_api_url | default(None) }}" + cluster_hostname: "{{ openshift_master_cluster_hostname | default(None) }}" + cluster_public_hostname: "{{ openshift_master_cluster_public_hostname | default(None) }}" + console_path: "{{ openshift_master_console_path | default(None) }}" + console_port: "{{ openshift_master_console_port | default(None) }}" + console_url: "{{ openshift_master_console_url | default(None) }}" + console_use_ssl: "{{ openshift_master_console_use_ssl | default(None) }}" + public_console_url: "{{ openshift_master_public_console_url | default(None) }}" + portal_net: "{{ openshift_master_portal_net | default(None) }}" + ha: "{{ openshift_master_ha | default(groups.oo_masters | length > 1) }}" + master_count: "{{ openshift_master_count | default(groups.oo_masters | length) }}" - openshift_facts: role: hosted openshift_env: @@ -319,13 +311,14 @@ file: path: "{{ named_certs_dir }}" state: directory + mode: 0700 when: named_certs_specified | bool - name: Land named certificates copy: src="{{ item.certfile }}" dest="{{ named_certs_dir }}" with_items: openshift_master_named_certificates when: named_certs_specified | bool - name: Land named certificate keys - copy: src="{{ item.keyfile }}" dest="{{ named_certs_dir }}" + copy: src="{{ item.keyfile }}" dest="{{ named_certs_dir }}" mode=0600 with_items: openshift_master_named_certificates when: named_certs_specified | bool diff --git a/playbooks/common/openshift-master/scaleup.yml b/playbooks/common/openshift-master/scaleup.yml index ccb1d23f1..6e6cb3e01 100644 --- a/playbooks/common/openshift-master/scaleup.yml +++ b/playbooks/common/openshift-master/scaleup.yml @@ -33,7 +33,8 @@ service: name={{ openshift.common.service_type }}-master-controllers state=restarted - name: verify api server command: > - curl -k --silent {{ openshift.master.api_url }}/healthz/ready + curl --silent --cacert {{ openshift.common.config_base }}/master/ca.crt + {{ openshift.master.api_url }}/healthz/ready register: api_available_output until: api_available_output.stdout == 'ok' retries: 120 diff --git a/playbooks/common/openshift-node/config.yml b/playbooks/common/openshift-node/config.yml index c62167bd3..7ca941732 100644 --- a/playbooks/common/openshift-node/config.yml +++ b/playbooks/common/openshift-node/config.yml @@ -1,10 +1,12 @@ --- - name: Gather and set facts for node hosts hosts: oo_nodes_to_config + vars: + t_oo_option_node_debug_level: "{{ lookup('oo_option', 'openshift_node_debug_level') }}" pre_tasks: - set_fact: - openshift_node_debug_level: "{{ lookup('oo_option', 'openshift_node_debug_level') | default(openshift.common.debug_level, true) }}" - when: openshift_node_debug_level is not defined + openshift_node_debug_level: "{{ t_oo_option_node_debug_level }}" + when: openshift_node_debug_level is not defined and t_oo_option_node_debug_level != "" roles: - openshift_facts tasks: @@ -12,20 +14,11 @@ # configured, we need to make sure to set the node properties beforehand if # we do not want the defaults - openshift_facts: - role: "{{ item.role }}" - local_facts: "{{ item.local_facts }}" - with_items: - - role: common - local_facts: - hostname: "{{ openshift_hostname | default(None) }}" - public_hostname: "{{ openshift_public_hostname | default(None) }}" - deployment_type: "{{ openshift_deployment_type }}" - use_flannel: "{{ openshift_use_flannel | default(None) }}" - - role: node - local_facts: - labels: "{{ openshift_node_labels | default(None) }}" - annotations: "{{ openshift_node_annotations | default(None) }}" - schedulable: "{{ openshift_schedulable | default(openshift_scheduleable) | default(None) }}" + role: node + local_facts: + labels: "{{ openshift_node_labels | default(None) }}" + annotations: "{{ openshift_node_annotations | default(None) }}" + schedulable: "{{ openshift_schedulable | default(openshift_scheduleable) | default(None) }}" - name: Check status of node certificates stat: path: "{{ openshift.common.config_base }}/node/{{ item }}" @@ -43,22 +36,6 @@ node_subdir: node-{{ openshift.common.hostname }} config_dir: "{{ openshift.common.config_base }}/generated-configs/node-{{ openshift.common.hostname }}" node_cert_dir: "{{ openshift.common.config_base }}/node" - - name: Check status of flannel external etcd certificates - stat: - path: "{{ openshift.common.config_base }}/node/{{ item }}" - with_items: - - node.etcd-client.crt - - node.etcd-ca.crt - register: g_external_etcd_flannel_cert_stat_result - when: groups.oo_etcd_to_config is defined and groups.oo_etcd_to_config and (openshift.common.use_flannel | bool) - - set_fact: - etcd_client_flannel_certs_missing: "{{ g_external_etcd_flannel_cert_stat_result.results - | oo_collect(attribute='stat.exists') - | list | intersect([false])}}" - etcd_cert_subdir: openshift-node-{{ openshift.common.hostname }} - etcd_cert_config_dir: "{{ openshift.common.config_base }}/node" - etcd_cert_prefix: node.etcd- - when: groups.oo_etcd_to_config is defined and groups.oo_etcd_to_config and (openshift.common.use_flannel | bool) - name: Create temp directory for syncing certs hosts: localhost @@ -71,65 +48,6 @@ register: mktemp changed_when: False -- name: Configure flannel etcd certificates - hosts: oo_first_etcd - vars: - etcd_generated_certs_dir: /etc/etcd/generated_certs - sync_tmpdir: "{{ hostvars.localhost.mktemp.stdout }}" - pre_tasks: - - set_fact: - etcd_needing_client_certs: "{{ hostvars - | oo_select_keys(groups['oo_nodes_to_config']) - | oo_filter_list(filter_attr='etcd_client_flannel_certs_missing') | default([]) }}" - when: etcd_client_flannel_certs_missing is defined and etcd_client_flannel_certs_missing - roles: - - role: etcd_certificates - when: openshift_use_flannel | default(false) | bool - post_tasks: - - name: Create a tarball of the etcd flannel certs - command: > - tar -czvf {{ etcd_generated_certs_dir }}/{{ item.etcd_cert_subdir }}.tgz - -C {{ etcd_generated_certs_dir }}/{{ item.etcd_cert_subdir }} . - args: - creates: "{{ etcd_generated_certs_dir }}/{{ item.etcd_cert_subdir }}.tgz" - with_items: etcd_needing_client_certs - when: etcd_client_flannel_certs_missing is defined and etcd_client_flannel_certs_missing - - name: Retrieve the etcd cert tarballs - fetch: - src: "{{ etcd_generated_certs_dir }}/{{ item.etcd_cert_subdir }}.tgz" - dest: "{{ sync_tmpdir }}/" - flat: yes - fail_on_missing: yes - validate_checksum: yes - with_items: etcd_needing_client_certs - when: etcd_client_flannel_certs_missing is defined and etcd_client_flannel_certs_missing - -- name: Copy the external etcd flannel certs to the nodes - hosts: oo_nodes_to_config - vars: - sync_tmpdir: "{{ hostvars.localhost.mktemp.stdout }}" - tasks: - - name: Ensure certificate directory exists - file: - path: "{{ openshift.common.config_base }}/node" - state: directory - when: etcd_client_flannel_certs_missing is defined and etcd_client_flannel_certs_missing - - name: Unarchive the tarball on the master - unarchive: - src: "{{ sync_tmpdir }}/{{ etcd_cert_subdir }}.tgz" - dest: "{{ etcd_cert_config_dir }}" - when: etcd_client_flannel_certs_missing is defined and etcd_client_flannel_certs_missing - - file: - path: "{{ etcd_cert_config_dir }}/{{ item }}" - owner: root - group: root - mode: 0600 - with_items: - - node.etcd-client.crt - - node.etcd-client.key - - node.etcd-ca.crt - when: etcd_client_flannel_certs_missing is defined and etcd_client_flannel_certs_missing - - name: Create node certificates hosts: oo_first_master vars: @@ -197,6 +115,11 @@ vars: openshift_node_master_api_url: "{{ hostvars[groups.oo_first_master.0].openshift.master.api_url }}" openshift_node_first_master_ip: "{{ hostvars[groups.oo_first_master.0].openshift.common.ip }}" + # TODO: configure these based on + # hostvars[groups.oo_first_master.0].openshift.hosted.registry instead of + # hardcoding + openshift_docker_hosted_registry_insecure: True + openshift_docker_hosted_registry_network: "{{ hostvars[groups.oo_first_master.0].openshift.master.portal_net }}" roles: - openshift_node @@ -205,9 +128,94 @@ vars: openshift_node_master_api_url: "{{ hostvars[groups.oo_first_master.0].openshift.master.api_url }}" openshift_node_first_master_ip: "{{ hostvars[groups.oo_first_master.0].openshift.common.ip }}" + # TODO: configure these based on + # hostvars[groups.oo_first_master.0].openshift.hosted.registry instead of + # hardcoding + openshift_docker_hosted_registry_insecure: True + openshift_docker_hosted_registry_network: "{{ hostvars[groups.oo_first_master.0].openshift.master.portal_net }}" roles: - openshift_node +- name: Gather and set facts for flannel certificatess + hosts: oo_nodes_to_config + tasks: + - name: Check status of flannel external etcd certificates + stat: + path: "{{ openshift.common.config_base }}/node/{{ item }}" + with_items: + - node.etcd-client.crt + - node.etcd-ca.crt + register: g_external_etcd_flannel_cert_stat_result + when: groups.oo_etcd_to_config is defined and groups.oo_etcd_to_config and (openshift.common.use_flannel | bool) + - set_fact: + etcd_client_flannel_certs_missing: "{{ g_external_etcd_flannel_cert_stat_result.results + | oo_collect(attribute='stat.exists') + | list | intersect([false])}}" + etcd_cert_subdir: openshift-node-{{ openshift.common.hostname }} + etcd_cert_config_dir: "{{ openshift.common.config_base }}/node" + etcd_cert_prefix: node.etcd- + when: groups.oo_etcd_to_config is defined and groups.oo_etcd_to_config and (openshift.common.use_flannel | bool) + +- name: Configure flannel etcd certificates + hosts: oo_first_etcd + vars: + etcd_generated_certs_dir: /etc/etcd/generated_certs + sync_tmpdir: "{{ hostvars.localhost.mktemp.stdout }}" + pre_tasks: + - set_fact: + etcd_needing_client_certs: "{{ hostvars + | oo_select_keys(groups['oo_nodes_to_config']) + | oo_filter_list(filter_attr='etcd_client_flannel_certs_missing') | default([]) }}" + when: etcd_client_flannel_certs_missing is defined and etcd_client_flannel_certs_missing + roles: + - role: etcd_certificates + when: openshift_use_flannel | default(false) | bool + post_tasks: + - name: Create a tarball of the etcd flannel certs + command: > + tar -czvf {{ etcd_generated_certs_dir }}/{{ item.etcd_cert_subdir }}.tgz + -C {{ etcd_generated_certs_dir }}/{{ item.etcd_cert_subdir }} . + args: + creates: "{{ etcd_generated_certs_dir }}/{{ item.etcd_cert_subdir }}.tgz" + with_items: etcd_needing_client_certs + when: etcd_client_flannel_certs_missing is defined and etcd_client_flannel_certs_missing + - name: Retrieve the etcd cert tarballs + fetch: + src: "{{ etcd_generated_certs_dir }}/{{ item.etcd_cert_subdir }}.tgz" + dest: "{{ sync_tmpdir }}/" + flat: yes + fail_on_missing: yes + validate_checksum: yes + with_items: etcd_needing_client_certs + when: etcd_client_flannel_certs_missing is defined and etcd_client_flannel_certs_missing + +- name: Copy the external etcd flannel certs to the nodes + hosts: oo_nodes_to_config + vars: + sync_tmpdir: "{{ hostvars.localhost.mktemp.stdout }}" + tasks: + - name: Ensure certificate directory exists + file: + path: "{{ openshift.common.config_base }}/node" + state: directory + when: etcd_client_flannel_certs_missing is defined and etcd_client_flannel_certs_missing + - name: Unarchive the tarball on the master + unarchive: + src: "{{ sync_tmpdir }}/{{ etcd_cert_subdir }}.tgz" + dest: "{{ etcd_cert_config_dir }}" + when: etcd_client_flannel_certs_missing is defined and etcd_client_flannel_certs_missing + - file: + path: "{{ etcd_cert_config_dir }}/{{ item }}" + owner: root + group: root + mode: 0600 + with_items: + - node.etcd-client.crt + - node.etcd-client.key + - node.etcd-ca.crt + when: etcd_client_flannel_certs_missing is defined and etcd_client_flannel_certs_missing + + - name: Additional node config hosts: oo_nodes_to_config vars: @@ -258,7 +266,8 @@ # Using curl here since the uri module requires python-httplib2 and # wait_for port doesn't provide health information. command: > - curl -k --silent {{ openshift.master.api_url }}/healthz/ready + curl --silent --cacert {{ openshift.common.config_base }}/master/ca.crt + {{ openshift.master.api_url }}/healthz/ready register: api_available_output until: api_available_output.stdout == 'ok' retries: 120 diff --git a/playbooks/libvirt/openshift-cluster/launch.yml b/playbooks/libvirt/openshift-cluster/launch.yml index 3a48c82bc..701d57d26 100644 --- a/playbooks/libvirt/openshift-cluster/launch.yml +++ b/playbooks/libvirt/openshift-cluster/launch.yml @@ -7,9 +7,6 @@ vars_files: - vars.yml vars: - os_libvirt_storage_pool: "{{ libvirt_storage_pool | default('images') }}" - os_libvirt_storage_pool_path: "{{ libvirt_storage_pool_path | default('/var/lib/libvirt/images') }}" - os_libvirt_network: "{{ libvirt_network | default('default') }}" image_url: "{{ deployment_vars[deployment_type].image.url }}" image_sha256: "{{ deployment_vars[deployment_type].image.sha256 }}" image_name: "{{ deployment_vars[deployment_type].image.name }}" diff --git a/playbooks/libvirt/openshift-cluster/tasks/launch_instances.yml b/playbooks/libvirt/openshift-cluster/tasks/launch_instances.yml index b00352539..937a765fa 100644 --- a/playbooks/libvirt/openshift-cluster/tasks/launch_instances.yml +++ b/playbooks/libvirt/openshift-cluster/tasks/launch_instances.yml @@ -13,54 +13,58 @@ get_url: url: '{{ image_url }}' sha256sum: '{{ image_sha256 }}' - dest: '{{ os_libvirt_storage_pool_path }}/{{ [image_name, image_compression] | reject("equalto", "") | join(".") }}' + dest: '{{ libvirt_storage_pool_path }}/{{ [image_name, image_compression] | difference([""]) | join(".") }}' when: '{{ ( lookup("oo_option", "skip_image_download") | default("no", True) | lower ) in ["false", "no"] }}' register: downloaded_image - name: Uncompress xz compressed base cloud image - command: 'unxz -kf {{ os_libvirt_storage_pool_path }}/{{ [image_name, image_compression] | join(".") }}' + command: 'unxz -kf {{ libvirt_storage_pool_path }}/{{ [image_name, image_compression] | join(".") }}' args: - creates: '{{ os_libvirt_storage_pool_path }}/{{ image_name }}' + creates: '{{ libvirt_storage_pool_path }}/{{ image_name }}' when: image_compression in ["xz"] and downloaded_image.changed - name: Uncompress tgz compressed base cloud image - command: 'tar zxvf {{ os_libvirt_storage_pool_path }}/{{ [image_name, image_compression] | join(".") }}' + command: 'tar zxvf {{ libvirt_storage_pool_path }}/{{ [image_name, image_compression] | join(".") }}' args: - creates: '{{ os_libvirt_storage_pool_path }}/{{ image_name }}' + creates: '{{ libvirt_storage_pool_path }}/{{ image_name }}' when: image_compression in ["tgz"] and downloaded_image.changed - name: Uncompress gzip compressed base cloud image - command: 'gunzip {{ os_libvirt_storage_pool_path }}/{{ [image_name, image_compression] | join(".") }}' + command: 'gunzip {{ libvirt_storage_pool_path }}/{{ [image_name, image_compression] | join(".") }}' args: - creates: '{{ os_libvirt_storage_pool_path }}/{{ image_name }}' + creates: '{{ libvirt_storage_pool_path }}/{{ image_name }}' when: image_compression in ["gz"] and downloaded_image.changed - name: Create the cloud-init config drive path file: - dest: '{{ os_libvirt_storage_pool_path }}/{{ item }}_configdrive/' + dest: '{{ libvirt_storage_pool_path }}/{{ item }}_configdrive/' state: directory with_items: instances - name: Create the cloud-init config drive files template: src: '{{ item[1] }}' - dest: '{{ os_libvirt_storage_pool_path }}/{{ item[0] }}_configdrive/{{ item[1] }}' + dest: '{{ libvirt_storage_pool_path }}/{{ item[0] }}_configdrive/{{ item[1] }}' with_nested: - instances - [ user-data, meta-data ] - name: Create the cloud-init config drive - command: 'genisoimage -output {{ os_libvirt_storage_pool_path }}/{{ item }}_cloud-init.iso -volid cidata -joliet -rock user-data meta-data' + command: 'genisoimage -output {{ libvirt_storage_pool_path }}/{{ item }}_cloud-init.iso -volid cidata -joliet -rock user-data meta-data' args: - chdir: '{{ os_libvirt_storage_pool_path }}/{{ item }}_configdrive/' - creates: '{{ os_libvirt_storage_pool_path }}/{{ item }}_cloud-init.iso' + chdir: '{{ libvirt_storage_pool_path }}/{{ item }}_configdrive/' + creates: '{{ libvirt_storage_pool_path }}/{{ item }}_cloud-init.iso' with_items: instances - name: Refresh the libvirt storage pool for openshift command: 'virsh -c {{ libvirt_uri }} pool-refresh {{ libvirt_storage_pool }}' -- name: Create VMs drives - command: 'virsh -c {{ libvirt_uri }} vol-create-as {{ os_libvirt_storage_pool }} {{ item }}.qcow2 10G --format qcow2 --backing-vol {{ image_name }} --backing-vol-format qcow2' +- name: Create VM drives + command: 'virsh -c {{ libvirt_uri }} vol-create-as {{ libvirt_storage_pool }} {{ item }}.qcow2 10G --format qcow2 --backing-vol {{ image_name }} --backing-vol-format qcow2' + with_items: instances + +- name: Create VM docker drives + command: 'virsh -c {{ libvirt_uri }} vol-create-as {{ libvirt_storage_pool }} {{ item }}-docker.qcow2 10G --format qcow2 --allocation 0' with_items: instances - name: Create VMs diff --git a/playbooks/libvirt/openshift-cluster/templates/domain.xml b/playbooks/libvirt/openshift-cluster/templates/domain.xml index 0ca8e0974..8e96cec8d 100644 --- a/playbooks/libvirt/openshift-cluster/templates/domain.xml +++ b/playbooks/libvirt/openshift-cluster/templates/domain.xml @@ -1,6 +1,6 @@ <domain type='kvm' id='8'> <name>{{ item }}</name> - <memory unit='GiB'>1</memory> + <memory unit='MiB'>{{ libvirt_instance_memory_mib }}</memory> <metadata xmlns:ansible="https://github.com/ansible/ansible"> <ansible:tags> <ansible:tag>environment-{{ cluster_env }}</ansible:tag> @@ -9,8 +9,7 @@ <ansible:tag>sub-host-type-{{ g_sub_host_type }}</ansible:tag> </ansible:tags> </metadata> - <currentMemory unit='GiB'>1</currentMemory> - <vcpu placement='static'>2</vcpu> + <vcpu placement='static'>{{ libvirt_instance_vcpu }}</vcpu> <os> <type arch='x86_64' machine='pc'>hvm</type> <boot dev='hd'/> @@ -32,18 +31,23 @@ <emulator>/usr/bin/qemu-system-x86_64</emulator> <disk type='file' device='disk'> <driver name='qemu' type='qcow2'/> - <source file='{{ os_libvirt_storage_pool_path }}/{{ item }}.qcow2'/> + <source file='{{ libvirt_storage_pool_path }}/{{ item }}.qcow2'/> <target dev='vda' bus='virtio'/> </disk> + <disk type='file' device='disk'> + <driver name='qemu' type='qcow2'/> + <source file='{{ libvirt_storage_pool_path }}/{{ item }}-docker.qcow2'/> + <target dev='vdb' bus='virtio'/> + </disk> <disk type='file' device='cdrom'> <driver name='qemu' type='raw'/> - <source file='{{ os_libvirt_storage_pool_path }}/{{ item }}_cloud-init.iso'/> - <target dev='vdb' bus='virtio'/> + <source file='{{ libvirt_storage_pool_path }}/{{ item }}_cloud-init.iso'/> + <target dev='vdc' bus='virtio'/> <readonly/> </disk> <controller type='usb' index='0' /> <interface type='network'> - <source network='{{ os_libvirt_network }}'/> + <source network='{{ libvirt_network }}'/> <model type='virtio'/> </interface> <serial type='pty'> diff --git a/playbooks/libvirt/openshift-cluster/templates/user-data b/playbooks/libvirt/openshift-cluster/templates/user-data index ead881f78..8b79940f4 100644 --- a/playbooks/libvirt/openshift-cluster/templates/user-data +++ b/playbooks/libvirt/openshift-cluster/templates/user-data @@ -4,6 +4,9 @@ disable_root: true hostname: {{ item[0] }} fqdn: {{ item[0] }}.example.com +mounts: +- [ vdb ] + users: - default - name: root @@ -23,6 +26,12 @@ write_files: permissions: 440 content: | Defaults:openshift !requiretty + - content: | + DEVS=/dev/vdb + VG=docker_vg + path: /etc/sysconfig/docker-storage-setup + owner: root:root + permissions: '0644' runcmd: - NETWORK_CONFIG=/etc/sysconfig/network-scripts/ifcfg-eth0; if ! grep DHCP_HOSTNAME ${NETWORK_CONFIG}; then echo 'DHCP_HOSTNAME="{{ item[0] }}.example.com"' >> ${NETWORK_CONFIG}; fi; pkill -9 dhclient; service network restart diff --git a/playbooks/libvirt/openshift-cluster/terminate.yml b/playbooks/libvirt/openshift-cluster/terminate.yml index f4749c28d..d6251ac83 100644 --- a/playbooks/libvirt/openshift-cluster/terminate.yml +++ b/playbooks/libvirt/openshift-cluster/terminate.yml @@ -45,12 +45,18 @@ - groups['oo_hosts_to_terminate'] - [ destroy, undefine ] - - name: Delete VMs drives + - name: Delete VM drives command: 'virsh -c {{ libvirt_uri }} vol-delete --pool {{ libvirt_storage_pool }} {{ item }}.qcow2' args: removes: '{{ libvirt_storage_pool_path }}/{{ item }}.qcow2' with_items: groups['oo_hosts_to_terminate'] + - name: Delete VM docker drives + command: 'virsh -c {{ libvirt_uri }} vol-delete --pool {{ libvirt_storage_pool }} {{ item }}-docker.qcow2' + args: + removes: '{{ libvirt_storage_pool_path }}/{{ item }}-docker.qcow2' + with_items: groups['oo_hosts_to_terminate'] + - name: Delete the VM cloud-init image file: path: '{{ libvirt_storage_pool_path }}/{{ item }}_cloud-init.iso' diff --git a/playbooks/libvirt/openshift-cluster/vars.yml b/playbooks/libvirt/openshift-cluster/vars.yml index f28245f88..aa0c69e08 100644 --- a/playbooks/libvirt/openshift-cluster/vars.yml +++ b/playbooks/libvirt/openshift-cluster/vars.yml @@ -1,8 +1,11 @@ --- -libvirt_storage_pool_path: "{{ lookup('env','HOME') }}/libvirt-storage-pool-openshift-ansible" -libvirt_storage_pool: 'openshift-ansible' -libvirt_network: openshift-ansible -libvirt_uri: 'qemu:///system' +default_pool_path: "{{ lookup('env','HOME') }}/libvirt-storage-pool-openshift-ansible" +libvirt_storage_pool_path: "{{ lookup('oo_option', 'libvirt_storage_pool_path') | default(default_pool_path, True) }}" +libvirt_storage_pool: "{{ lookup('oo_option', 'libvirt_storage_pool') | default('openshift-ansible', True) }}" +libvirt_network: "{{ lookup('oo_option', 'libvirt_network') | default('openshift-ansible', True) }}" +libvirt_instance_memory_mib: "{{ lookup('oo_option', 'libvirt_instance_memory_mib') | default(1024, True) }}" +libvirt_instance_vcpu: "{{ lookup('oo_option', 'libvirt_instance_vcpu') | default(2, True) }}" +libvirt_uri: "{{ lookup('oo_option', 'libvirt_uri') | default('qemu:///system', True) }}" debug_level: 2 # Automatic download of the qcow2 image for RHEL cannot be done directly from the RedHat portal because it requires authentication. diff --git a/playbooks/openstack/openshift-cluster/files/heat_stack.yaml b/playbooks/openstack/openshift-cluster/files/heat_stack.yaml index af774aa32..dd6a22cbe 100644 --- a/playbooks/openstack/openshift-cluster/files/heat_stack.yaml +++ b/playbooks/openstack/openshift-cluster/files/heat_stack.yaml @@ -598,6 +598,10 @@ resources: template: | #cloud-config write_files: + - path: /etc/sudoers.d/00-openshift-no-requiretty + permissions: 440 + content: | + Defaults:openshift !requiretty - path: /etc/sysconfig/network-scripts/ifcfg-eth0 content: | DEVICE="eth0" diff --git a/playbooks/openstack/openshift-cluster/files/user-data b/playbooks/openstack/openshift-cluster/files/user-data index e789a5b69..eb65f7cec 100644 --- a/playbooks/openstack/openshift-cluster/files/user-data +++ b/playbooks/openstack/openshift-cluster/files/user-data @@ -5,3 +5,9 @@ system_info: default_user: name: openshift sudo: ["ALL=(ALL) NOPASSWD: ALL"] + +write_files: + - path: /etc/sudoers.d/00-openshift-no-requiretty + permissions: 440 + content: | + Defaults:openshift !requiretty diff --git a/roles/docker/defaults/main.yml b/roles/docker/defaults/main.yml new file mode 100644 index 000000000..1b26af0dd --- /dev/null +++ b/roles/docker/defaults/main.yml @@ -0,0 +1,2 @@ +--- +docker_version: ''
\ No newline at end of file diff --git a/roles/docker/handlers/main.yml b/roles/docker/handlers/main.yml index 9f827417f..aff905bc8 100644 --- a/roles/docker/handlers/main.yml +++ b/roles/docker/handlers/main.yml @@ -4,7 +4,7 @@ service: name: docker state: restarted - when: not docker_service_status_changed | default(false) + when: not docker_service_status_changed | default(false) | bool - name: restart udev service: diff --git a/roles/docker/tasks/main.yml b/roles/docker/tasks/main.yml index a56f1f391..9cf949d65 100644 --- a/roles/docker/tasks/main.yml +++ b/roles/docker/tasks/main.yml @@ -1,9 +1,30 @@ --- # tasks file for docker + +- name: Get current installed version if docker_version is specified + command: "{{ repoquery_cmd }} --installed --qf '%{version}' docker" + when: not openshift.common.is_atomic | bool and docker_version != '' + register: docker_version_result + changed_when: false + +- name: Downgrade docker if necessary + command: "{{ ansible_pkg_mgr }} downgrade -y docker-{{ docker_version }}" + register: docker_downgrade_result + when: not docker_version_result | skipped and docker_version_result | default('0.0', True) | version_compare(docker_version, 'gt') + - name: Install docker - action: "{{ ansible_pkg_mgr }} name=docker state=present" - when: not openshift.common.is_atomic | bool - + action: "{{ ansible_pkg_mgr }} name=docker{{ '-' + docker_version if docker_version != '' else '' }} state=present" + when: not openshift.common.is_atomic | bool and not docker_downgrade_result | changed + +- stat: path=/etc/sysconfig/docker + register: docker_check + when: docker_downgrade_result | changed + +- name: Remove deferred deletion for downgrades from 1.9 + command: > + sed -i 's/--storage-opt dm.use_deferred_deletion=true//' /etc/sysconfig/docker-storage + when: docker_downgrade_result | changed and docker_check.stat.exists | bool and docker_version_result | default('0.0', True) | version_compare('1.9', '>=') and docker_version | version_compare('1.9', '<') + - name: enable and start the docker service service: name: docker @@ -15,4 +36,42 @@ docker_service_status_changed: start_result | changed - include: udev_workaround.yml - when: docker_udev_workaround | default(False) + when: docker_udev_workaround | default(False) | bool + +- stat: path=/etc/sysconfig/docker + register: docker_check + +- name: Set registry params + lineinfile: + dest: /etc/sysconfig/docker + regexp: '^{{ item.reg_conf_var }}=.*$' + line: "{{ item.reg_conf_var }}='{{ item.reg_fact_val | oo_prepend_strings_in_list(item.reg_flag ~ ' ') | join(' ') }}'" + when: item.reg_fact_val != '' and docker_check.stat.isreg + with_items: + - reg_conf_var: ADD_REGISTRY + reg_fact_val: "{{ docker_additional_registries | default(None, true)}}" + reg_flag: --add-registry + - reg_conf_var: BLOCK_REGISTRY + reg_fact_val: "{{ docker_blocked_registries| default(None, true) }}" + reg_flag: --block-registry + - reg_conf_var: INSECURE_REGISTRY + reg_fact_val: "{{ docker_insecure_registries| default(None, true) }}" + reg_flag: --insecure-registry + notify: + - restart docker + +- name: Set various docker options + lineinfile: + dest: /etc/sysconfig/docker + regexp: '^OPTIONS=.*$' + line: "OPTIONS='\ + {% if ansible_selinux and ansible_selinux.status == '''enabled''' %} --selinux-enabled{% endif %}\ + {% if docker_log_driver is defined %} --log-driver {{ docker_log_driver }}{% endif %}\ + {% if docker_log_options is defined %} {{ docker_log_options | oo_split() | oo_prepend_strings_in_list('--log-opt ') | join(' ')}}{% endif %}\ + {% if docker_options is defined %} {{ docker_options }}{% endif %}\ + {% if docker_disable_push_dockerhub is defined %} --confirm-def-push={{ docker_disable_push_dockerhub | bool }}{% endif %}'" + when: docker_check.stat.isreg + notify: + - restart docker + +- meta: flush_handlers diff --git a/roles/docker/vars/main.yml b/roles/docker/vars/main.yml index 162487545..606cdb9b9 100644 --- a/roles/docker/vars/main.yml +++ b/roles/docker/vars/main.yml @@ -1,3 +1,3 @@ --- - +repoquery_cmd: "{{ 'dnf repoquery --latest-limit 1 -d 0' if ansible_pkg_mgr == 'dnf' else 'repoquery' }}" udevw_udevd_dir: /etc/systemd/system/systemd-udevd.service.d diff --git a/roles/etcd/tasks/main.yml b/roles/etcd/tasks/main.yml index e72509c4d..afec6b30b 100644 --- a/roles/etcd/tasks/main.yml +++ b/roles/etcd/tasks/main.yml @@ -36,9 +36,18 @@ state: stopped enabled: no +- name: Check for etcd service presence + command: systemctl show etcd.service + register: etcd_show + changed_when: false + +- name: Mask system etcd when containerized + when: openshift.common.is_containerized | bool and 'LoadState=not-found' not in etcd_show.stdout + command: systemctl mask etcd + - name: Reload systemd units command: systemctl daemon-reload - when: openshift.common.is_containerized and ( install_etcd_result | changed ) + when: openshift.common.is_containerized | bool and ( install_etcd_result | changed ) - name: Validate permissions on the config dir file: diff --git a/roles/etcd/templates/etcd.docker.service b/roles/etcd/templates/etcd.docker.service index 8058fa188..b8dbefa64 100644 --- a/roles/etcd/templates/etcd.docker.service +++ b/roles/etcd/templates/etcd.docker.service @@ -1,13 +1,16 @@ [Unit] Description=The Etcd Server container After=docker.service +Requires=docker.service +PartOf=docker.service [Service] EnvironmentFile=/etc/etcd/etcd.conf ExecStartPre=-/usr/bin/docker rm -f {{ etcd_service }} ExecStart=/usr/bin/docker run --name {{ etcd_service }} --rm -v /var/lib/etcd:/var/lib/etcd:z -v /etc/etcd:/etc/etcd:z --env-file=/etc/etcd/etcd.conf --net=host --entrypoint=/usr/bin/etcd {{ openshift.etcd.etcd_image }} ExecStop=/usr/bin/docker stop {{ etcd_service }} +SyslogIdentifier=etcd_container Restart=always [Install] -WantedBy=multi-user.target +WantedBy=docker.service diff --git a/roles/lib_openshift_api/build/ansible/edit.py b/roles/lib_openshift_api/build/ansible/edit.py new file mode 100644 index 000000000..943fa47a6 --- /dev/null +++ b/roles/lib_openshift_api/build/ansible/edit.py @@ -0,0 +1,84 @@ +# pylint: skip-file + +def main(): + ''' + ansible oc module for services + ''' + + module = AnsibleModule( + argument_spec=dict( + kubeconfig=dict(default='/etc/origin/master/admin.kubeconfig', type='str'), + state=dict(default='present', type='str', + choices=['present']), + debug=dict(default=False, type='bool'), + namespace=dict(default='default', type='str'), + name=dict(default=None, required=True, type='str'), + kind=dict(required=True, + type='str', + choices=['dc', 'deploymentconfig', + 'svc', 'service', + 'scc', 'securitycontextconstraints', + 'ns', 'namespace', 'project', 'projects', + 'is', 'imagestream', + 'istag', 'imagestreamtag', + 'bc', 'buildconfig', + 'routes', + 'node', + 'secret', + ]), + file_name=dict(default=None, type='str'), + file_format=dict(default='yaml', type='str'), + content=dict(default=None, required=True, type='dict'), + force=dict(default=False, type='bool'), + ), + supports_check_mode=True, + ) + ocedit = Edit(module.params['kind'], + module.params['namespace'], + module.params['name'], + kubeconfig=module.params['kubeconfig'], + verbose=module.params['debug']) + + state = module.params['state'] + + api_rval = ocedit.get() + + ######## + # Create + ######## + if not Utils.exists(api_rval['results'], module.params['name']): + module.fail_json(msg=api_rval) + + ######## + # Update + ######## + api_rval = ocedit.update(module.params['file_name'], + module.params['content'], + module.params['force'], + module.params['file_format']) + + + if api_rval['returncode'] != 0: + module.fail_json(msg=api_rval) + + if api_rval.has_key('updated') and not api_rval['updated']: + module.exit_json(changed=False, results=api_rval, state="present") + + # return the created object + api_rval = ocedit.get() + + if api_rval['returncode'] != 0: + module.fail_json(msg=api_rval) + + module.exit_json(changed=True, results=api_rval, state="present") + + module.exit_json(failed=True, + changed=False, + results='Unknown state passed. %s' % state, + state="unknown") + +# pylint: disable=redefined-builtin, unused-wildcard-import, wildcard-import, locally-disabled +# import module snippets. This are required +from ansible.module_utils.basic import * + +main() diff --git a/roles/lib_openshift_api/build/ansible/obj.py b/roles/lib_openshift_api/build/ansible/obj.py new file mode 100644 index 000000000..a14ac0e43 --- /dev/null +++ b/roles/lib_openshift_api/build/ansible/obj.py @@ -0,0 +1,139 @@ +# pylint: skip-file + +# pylint: disable=too-many-branches +def main(): + ''' + ansible oc module for services + ''' + + module = AnsibleModule( + argument_spec=dict( + kubeconfig=dict(default='/etc/origin/master/admin.kubeconfig', type='str'), + state=dict(default='present', type='str', + choices=['present', 'absent', 'list']), + debug=dict(default=False, type='bool'), + namespace=dict(default='default', type='str'), + name=dict(default=None, type='str'), + files=dict(default=None, type='list'), + kind=dict(required=True, + type='str', + choices=['dc', 'deploymentconfig', + 'svc', 'service', + 'scc', 'securitycontextconstraints', + 'ns', 'namespace', 'project', 'projects', + 'is', 'imagestream', + 'istag', 'imagestreamtag', + 'bc', 'buildconfig', + 'routes', + 'node', + 'secret', + ]), + delete_after=dict(default=False, type='bool'), + content=dict(default=None, type='dict'), + force=dict(default=False, type='bool'), + ), + mutually_exclusive=[["content", "files"]], + + supports_check_mode=True, + ) + ocobj = OCObject(module.params['kind'], + module.params['namespace'], + module.params['name'], + kubeconfig=module.params['kubeconfig'], + verbose=module.params['debug']) + + state = module.params['state'] + + api_rval = ocobj.get() + + ##### + # Get + ##### + if state == 'list': + module.exit_json(changed=False, results=api_rval['results'], state="list") + + if not module.params['name']: + module.fail_json(msg='Please specify a name when state is absent|present.') + ######## + # Delete + ######## + if state == 'absent': + if not Utils.exists(api_rval['results'], module.params['name']): + module.exit_json(changed=False, state="absent") + + if module.check_mode: + module.exit_json(change=False, msg='Would have performed a delete.') + + api_rval = ocobj.delete() + module.exit_json(changed=True, results=api_rval, state="absent") + + if state == 'present': + ######## + # Create + ######## + if not Utils.exists(api_rval['results'], module.params['name']): + + if module.check_mode: + module.exit_json(change=False, msg='Would have performed a create.') + + # Create it here + api_rval = ocobj.create(module.params['files'], module.params['content']) + if api_rval['returncode'] != 0: + module.fail_json(msg=api_rval) + + # return the created object + api_rval = ocobj.get() + + if api_rval['returncode'] != 0: + module.fail_json(msg=api_rval) + + # Remove files + if module.params['files'] and module.params['delete_after']: + Utils.cleanup(module.params['files']) + + module.exit_json(changed=True, results=api_rval, state="present") + + ######## + # Update + ######## + # if a file path is passed, use it. + update = ocobj.needs_update(module.params['files'], module.params['content']) + if not isinstance(update, bool): + module.fail_json(msg=update) + + # No changes + if not update: + if module.params['files'] and module.params['delete_after']: + Utils.cleanup(module.params['files']) + + module.exit_json(changed=False, results=api_rval['results'][0], state="present") + + if module.check_mode: + module.exit_json(change=False, msg='Would have performed an update.') + + api_rval = ocobj.update(module.params['files'], + module.params['content'], + module.params['force']) + + + if api_rval['returncode'] != 0: + module.fail_json(msg=api_rval) + + # return the created object + api_rval = ocobj.get() + + if api_rval['returncode'] != 0: + module.fail_json(msg=api_rval) + + module.exit_json(changed=True, results=api_rval, state="present") + + module.exit_json(failed=True, + changed=False, + results='Unknown state passed. %s' % state, + state="unknown") + +# pylint: disable=redefined-builtin, unused-wildcard-import, wildcard-import, locally-disabled +# import module snippets. This are required +from ansible.module_utils.basic import * + +main() diff --git a/roles/lib_openshift_api/build/ansible/secret.py b/roles/lib_openshift_api/build/ansible/secret.py new file mode 100644 index 000000000..8df7bbc64 --- /dev/null +++ b/roles/lib_openshift_api/build/ansible/secret.py @@ -0,0 +1,121 @@ +# pylint: skip-file + +# pylint: disable=too-many-branches +def main(): + ''' + ansible oc module for secrets + ''' + + module = AnsibleModule( + argument_spec=dict( + kubeconfig=dict(default='/etc/origin/master/admin.kubeconfig', type='str'), + state=dict(default='present', type='str', + choices=['present', 'absent', 'list']), + debug=dict(default=False, type='bool'), + namespace=dict(default='default', type='str'), + name=dict(default=None, type='str'), + files=dict(default=None, type='list'), + delete_after=dict(default=False, type='bool'), + contents=dict(default=None, type='list'), + force=dict(default=False, type='bool'), + ), + mutually_exclusive=[["contents", "files"]], + + supports_check_mode=True, + ) + occmd = Secret(module.params['namespace'], + module.params['name'], + kubeconfig=module.params['kubeconfig'], + verbose=module.params['debug']) + + state = module.params['state'] + + api_rval = occmd.get() + + ##### + # Get + ##### + if state == 'list': + module.exit_json(changed=False, results=api_rval['results'], state="list") + + if not module.params['name']: + module.fail_json(msg='Please specify a name when state is absent|present.') + ######## + # Delete + ######## + if state == 'absent': + if not Utils.exists(api_rval['results'], module.params['name']): + module.exit_json(changed=False, state="absent") + + if module.check_mode: + module.exit_json(change=False, msg='Would have performed a delete.') + + api_rval = occmd.delete() + module.exit_json(changed=True, results=api_rval, state="absent") + + + if state == 'present': + if module.params['files']: + files = module.params['files'] + elif module.params['contents']: + files = Utils.create_files_from_contents(module.params['contents']) + else: + module.fail_json(msg='Either specify files or contents.') + + ######## + # Create + ######## + if not Utils.exists(api_rval['results'], module.params['name']): + + if module.check_mode: + module.exit_json(change=False, msg='Would have performed a create.') + + api_rval = occmd.create(module.params['files'], module.params['contents']) + + # Remove files + if files and module.params['delete_after']: + Utils.cleanup(files) + + module.exit_json(changed=True, results=api_rval, state="present") + + ######## + # Update + ######## + secret = occmd.prep_secret(module.params['files'], module.params['contents']) + + if secret['returncode'] != 0: + module.fail_json(msg=secret) + + if Utils.check_def_equal(secret['results'], api_rval['results'][0]): + + # Remove files + if files and module.params['delete_after']: + Utils.cleanup(files) + + module.exit_json(changed=False, results=secret['results'], state="present") + + if module.check_mode: + module.exit_json(change=False, msg='Would have performed an update.') + + api_rval = occmd.update(files, force=module.params['force']) + + # Remove files + if secret and module.params['delete_after']: + Utils.cleanup(files) + + if api_rval['returncode'] != 0: + module.fail_json(msg=api_rval) + + + module.exit_json(changed=True, results=api_rval, state="present") + + module.exit_json(failed=True, + changed=False, + results='Unknown state passed. %s' % state, + state="unknown") + +# pylint: disable=redefined-builtin, unused-wildcard-import, wildcard-import, locally-disabled +# import module snippets. This are required +from ansible.module_utils.basic import * + +main() diff --git a/roles/lib_openshift_api/build/generate.py b/roles/lib_openshift_api/build/generate.py new file mode 100755 index 000000000..cf3f61d2c --- /dev/null +++ b/roles/lib_openshift_api/build/generate.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +''' + Generate the openshift-ansible/roles/lib_openshift_cli/library/ modules. +''' + +import os + +# pylint: disable=anomalous-backslash-in-string +GEN_STR = "#!/usr/bin/env python\n" + \ + "# ___ ___ _ _ ___ ___ _ _____ ___ ___\n" + \ + "# / __| __| \| | __| _ \ /_\_ _| __| \\\n" + \ + "# | (_ | _|| .` | _|| / / _ \| | | _|| |) |\n" + \ + "# \___|___|_|\_|___|_|_\/_/_\_\_|_|___|___/_ _____\n" + \ + "# | \ / _ \ | \| |/ _ \_ _| | __| \_ _|_ _|\n" + \ + "# | |) | (_) | | .` | (_) || | | _|| |) | | | |\n" + \ + "# |___/ \___/ |_|\_|\___/ |_| |___|___/___| |_|\n" + +OPENSHIFT_ANSIBLE_PATH = os.path.dirname(os.path.realpath(__file__)) + + +FILES = {'oc_obj.py': ['src/base.py', + '../../lib_yaml_editor/build/src/yedit.py', + 'src/obj.py', + 'ansible/obj.py', + ], + 'oc_secret.py': ['src/base.py', + '../../lib_yaml_editor/build/src/yedit.py', + 'src/secret.py', + 'ansible/secret.py', + ], + 'oc_edit.py': ['src/base.py', + '../../lib_yaml_editor/build/src/yedit.py', + 'src/edit.py', + 'ansible/edit.py', + ], + } + + +def main(): + ''' combine the necessary files to create the ansible module ''' + library = os.path.join(OPENSHIFT_ANSIBLE_PATH, '..', 'library/') + for fname, parts in FILES.items(): + with open(os.path.join(library, fname), 'w') as afd: + afd.seek(0) + afd.write(GEN_STR) + for fpart in parts: + with open(os.path.join(OPENSHIFT_ANSIBLE_PATH, fpart)) as pfd: + # first line is pylint disable so skip it + for idx, line in enumerate(pfd): + if idx == 0 and 'skip-file' in line: + continue + + afd.write(line) + + +if __name__ == '__main__': + main() + + diff --git a/roles/lib_openshift_api/build/src/base.py b/roles/lib_openshift_api/build/src/base.py new file mode 100644 index 000000000..66831c4e2 --- /dev/null +++ b/roles/lib_openshift_api/build/src/base.py @@ -0,0 +1,273 @@ +# pylint: skip-file +''' + OpenShiftCLI class that wraps the oc commands in a subprocess +''' + +import atexit +import json +import os +import shutil +import subprocess +import re + +import yaml +# This is here because of a bug that causes yaml +# to incorrectly handle timezone info on timestamps +def timestamp_constructor(_, node): + '''return timestamps as strings''' + return str(node.value) +yaml.add_constructor(u'tag:yaml.org,2002:timestamp', timestamp_constructor) + +# pylint: disable=too-few-public-methods +class OpenShiftCLI(object): + ''' Class to wrap the oc command line tools ''' + def __init__(self, + namespace, + kubeconfig='/etc/origin/master/admin.kubeconfig', + verbose=False): + ''' Constructor for OpenshiftOC ''' + self.namespace = namespace + self.verbose = verbose + self.kubeconfig = kubeconfig + + # Pylint allows only 5 arguments to be passed. + # pylint: disable=too-many-arguments + def _replace_content(self, resource, rname, content, force=False): + ''' replace the current object with the content ''' + res = self._get(resource, rname) + if not res['results']: + return res + + fname = '/tmp/%s' % rname + yed = Yedit(fname, res['results'][0]) + changes = [] + for key, value in content.items(): + changes.append(yed.put(key, value)) + + if any([not change[0] for change in changes]): + return {'returncode': 0, 'updated': False} + + yed.write() + + atexit.register(Utils.cleanup, [fname]) + + return self._replace(fname, force) + + def _replace(self, fname, force=False): + '''return all pods ''' + cmd = ['-n', self.namespace, 'replace', '-f', fname] + if force: + cmd.append('--force') + return self.oc_cmd(cmd) + + def _create(self, fname): + '''return all pods ''' + return self.oc_cmd(['create', '-f', fname, '-n', self.namespace]) + + def _delete(self, resource, rname): + '''return all pods ''' + return self.oc_cmd(['delete', resource, rname, '-n', self.namespace]) + + def _get(self, resource, rname=None): + '''return a secret by name ''' + cmd = ['get', resource, '-o', 'json', '-n', self.namespace] + if rname: + cmd.append(rname) + + rval = self.oc_cmd(cmd, output=True) + + # Ensure results are retuned in an array + if rval.has_key('items'): + rval['results'] = rval['items'] + elif not isinstance(rval['results'], list): + rval['results'] = [rval['results']] + + return rval + + def oc_cmd(self, cmd, output=False): + '''Base command for oc ''' + #cmds = ['/usr/bin/oc', '--config', self.kubeconfig] + cmds = ['/usr/bin/oc'] + cmds.extend(cmd) + + rval = {} + results = '' + err = None + + if self.verbose: + print ' '.join(cmds) + + proc = subprocess.Popen(cmds, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env={'KUBECONFIG': self.kubeconfig}) + + proc.wait() + stdout = proc.stdout.read() + stderr = proc.stderr.read() + + rval = {"returncode": proc.returncode, + "results": results, + } + + if proc.returncode == 0: + if output: + try: + rval['results'] = json.loads(stdout) + except ValueError as err: + if "No JSON object could be decoded" in err.message: + err = err.message + + if self.verbose: + print stdout + print stderr + print + + if err: + rval.update({"err": err, + "stderr": stderr, + "stdout": stdout, + "cmd": cmds + }) + + else: + rval.update({"stderr": stderr, + "stdout": stdout, + "results": {}, + }) + + return rval + +class Utils(object): + ''' utilities for openshiftcli modules ''' + @staticmethod + def create_file(rname, data, ftype=None): + ''' create a file in tmp with name and contents''' + path = os.path.join('/tmp', rname) + with open(path, 'w') as fds: + if ftype == 'yaml': + fds.write(yaml.safe_dump(data, default_flow_style=False)) + + elif ftype == 'json': + fds.write(json.dumps(data)) + else: + fds.write(data) + + # Register cleanup when module is done + atexit.register(Utils.cleanup, [path]) + return path + + @staticmethod + def create_files_from_contents(data): + '''Turn an array of dict: filename, content into a files array''' + files = [] + + for sfile in data: + path = Utils.create_file(sfile['path'], sfile['content']) + files.append(path) + + return files + + @staticmethod + def cleanup(files): + '''Clean up on exit ''' + for sfile in files: + if os.path.exists(sfile): + if os.path.isdir(sfile): + shutil.rmtree(sfile) + elif os.path.isfile(sfile): + os.remove(sfile) + + + @staticmethod + def exists(results, _name): + ''' Check to see if the results include the name ''' + if not results: + return False + + + if Utils.find_result(results, _name): + return True + + return False + + @staticmethod + def find_result(results, _name): + ''' Find the specified result by name''' + rval = None + for result in results: + if result.has_key('metadata') and result['metadata']['name'] == _name: + rval = result + break + + return rval + + @staticmethod + def get_resource_file(sfile, sfile_type='yaml'): + ''' return the service file ''' + contents = None + with open(sfile) as sfd: + contents = sfd.read() + + if sfile_type == 'yaml': + contents = yaml.safe_load(contents) + elif sfile_type == 'json': + contents = json.loads(contents) + + return contents + + # Disabling too-many-branches. This is a yaml dictionary comparison function + # pylint: disable=too-many-branches,too-many-return-statements + @staticmethod + def check_def_equal(user_def, result_def, debug=False): + ''' Given a user defined definition, compare it with the results given back by our query. ''' + + # Currently these values are autogenerated and we do not need to check them + skip = ['metadata', 'status'] + + for key, value in result_def.items(): + if key in skip: + continue + + # Both are lists + if isinstance(value, list): + if not isinstance(user_def[key], list): + return False + + # lists should be identical + if value != user_def[key]: + return False + + # recurse on a dictionary + elif isinstance(value, dict): + if not isinstance(user_def[key], dict): + if debug: + print "dict returned false not instance of dict" + return False + + # before passing ensure keys match + api_values = set(value.keys()) - set(skip) + user_values = set(user_def[key].keys()) - set(skip) + if api_values != user_values: + if debug: + print api_values + print user_values + print "keys are not equal in dict" + return False + + result = Utils.check_def_equal(user_def[key], value, debug=debug) + if not result: + if debug: + print "dict returned false" + return False + + # Verify each key, value pair is the same + else: + if not user_def.has_key(key) or value != user_def[key]: + if debug: + print "value not equal; user_def does not have key" + print value + print user_def[key] + return False + + return True diff --git a/roles/lib_openshift_api/build/src/edit.py b/roles/lib_openshift_api/build/src/edit.py new file mode 100644 index 000000000..7020ace47 --- /dev/null +++ b/roles/lib_openshift_api/build/src/edit.py @@ -0,0 +1,49 @@ +# pylint: skip-file + +class Edit(OpenShiftCLI): + ''' Class to wrap the oc command line tools + ''' + # pylint: disable=too-many-arguments + def __init__(self, + kind, + namespace, + resource_name=None, + kubeconfig='/etc/origin/master/admin.kubeconfig', + verbose=False): + ''' Constructor for OpenshiftOC ''' + super(Edit, self).__init__(namespace, kubeconfig) + self.namespace = namespace + self.kind = kind + self.name = resource_name + self.kubeconfig = kubeconfig + self.verbose = verbose + + def get(self): + '''return a secret by name ''' + return self._get(self.kind, self.name) + + def update(self, file_name, content, force=False, content_type='yaml'): + '''run update ''' + if file_name: + if content_type == 'yaml': + data = yaml.load(open(file_name)) + elif content_type == 'json': + data = json.loads(open(file_name).read()) + + changes = [] + yed = Yedit(file_name, data) + for key, value in content.items(): + changes.append(yed.put(key, value)) + + if any([not change[0] for change in changes]): + return {'returncode': 0, 'updated': False} + + yed.write() + + atexit.register(Utils.cleanup, [file_name]) + + return self._replace(file_name, force=force) + + return self._replace_content(self.kind, self.name, content, force=force) + + diff --git a/roles/lib_openshift_api/build/src/obj.py b/roles/lib_openshift_api/build/src/obj.py new file mode 100644 index 000000000..a3ad4b3c4 --- /dev/null +++ b/roles/lib_openshift_api/build/src/obj.py @@ -0,0 +1,78 @@ +# pylint: skip-file + +class OCObject(OpenShiftCLI): + ''' Class to wrap the oc command line tools ''' + + # pylint allows 5. we need 6 + # pylint: disable=too-many-arguments + def __init__(self, + kind, + namespace, + rname=None, + kubeconfig='/etc/origin/master/admin.kubeconfig', + verbose=False): + ''' Constructor for OpenshiftOC ''' + super(OCObject, self).__init__(namespace, kubeconfig) + self.kind = kind + self.namespace = namespace + self.name = rname + self.kubeconfig = kubeconfig + self.verbose = verbose + + def get(self): + '''return a deploymentconfig by name ''' + return self._get(self.kind, rname=self.name) + + def delete(self): + '''return all pods ''' + return self._delete(self.kind, self.name) + + def create(self, files=None, content=None): + '''Create a deploymentconfig ''' + if files: + return self._create(files[0]) + + return self._create(Utils.create_files_from_contents(content)) + + + # pylint: disable=too-many-function-args + def update(self, files=None, content=None, force=False): + '''run update dc + + This receives a list of file names and takes the first filename and calls replace. + ''' + if files: + return self._replace(files[0], force) + + return self.update_content(content, force) + + def update_content(self, content, force=False): + '''update the dc with the content''' + return self._replace_content(self.kind, self.name, content, force=force) + + def needs_update(self, files=None, content=None, content_type='yaml'): + ''' check to see if we need to update ''' + objects = self.get() + if objects['returncode'] != 0: + return objects + + # pylint: disable=no-member + data = None + if files: + data = Utils.get_resource_file(files[0], content_type) + + # if equal then no need. So not equal is True + return not Utils.check_def_equal(data, objects['results'][0], True) + else: + data = content + + for key, value in data.items(): + if key == 'metadata': + continue + if not objects['results'][0].has_key(key): + return True + if value != objects['results'][0][key]: + return True + + return False + diff --git a/roles/lib_openshift_api/build/src/secret.py b/roles/lib_openshift_api/build/src/secret.py new file mode 100644 index 000000000..af61dfa01 --- /dev/null +++ b/roles/lib_openshift_api/build/src/secret.py @@ -0,0 +1,68 @@ +# pylint: skip-file + +class Secret(OpenShiftCLI): + ''' Class to wrap the oc command line tools + ''' + def __init__(self, + namespace, + secret_name=None, + kubeconfig='/etc/origin/master/admin.kubeconfig', + verbose=False): + ''' Constructor for OpenshiftOC ''' + super(Secret, self).__init__(namespace, kubeconfig) + self.namespace = namespace + self.name = secret_name + self.kubeconfig = kubeconfig + self.verbose = verbose + + def get(self): + '''return a secret by name ''' + return self._get('secrets', self.name) + + def delete(self): + '''delete a secret by name''' + return self._delete('secrets', self.name) + + def create(self, files=None, contents=None): + '''Create a secret ''' + if not files: + files = Utils.create_files_from_contents(contents) + + secrets = ["%s=%s" % (os.path.basename(sfile), sfile) for sfile in files] + cmd = ['-n%s' % self.namespace, 'secrets', 'new', self.name] + cmd.extend(secrets) + + return self.oc_cmd(cmd) + + def update(self, files, force=False): + '''run update secret + + This receives a list of file names and converts it into a secret. + The secret is then written to disk and passed into the `oc replace` command. + ''' + secret = self.prep_secret(files) + if secret['returncode'] != 0: + return secret + + sfile_path = '/tmp/%s' % self.name + with open(sfile_path, 'w') as sfd: + sfd.write(json.dumps(secret['results'])) + + atexit.register(Utils.cleanup, [sfile_path]) + + return self._replace(sfile_path, force=force) + + def prep_secret(self, files=None, contents=None): + ''' return what the secret would look like if created + This is accomplished by passing -ojson. This will most likely change in the future + ''' + if not files: + files = Utils.create_files_from_contents(contents) + + secrets = ["%s=%s" % (os.path.basename(sfile), sfile) for sfile in files] + cmd = ['-ojson', '-n%s' % self.namespace, 'secrets', 'new', self.name] + cmd.extend(secrets) + + return self.oc_cmd(cmd, output=True) + + diff --git a/roles/lib_openshift_api/build/test/README b/roles/lib_openshift_api/build/test/README new file mode 100644 index 000000000..af9f05b3d --- /dev/null +++ b/roles/lib_openshift_api/build/test/README @@ -0,0 +1,5 @@ +After generate.py has run, the ansible modules will be placed under ../../../openshift-ansible/roles/lib_openshift_api/library. + + +To run the tests you need to run them like this: +./services.yml -M ../../library diff --git a/roles/lib_openshift_api/build/test/deploymentconfig.yml b/roles/lib_openshift_api/build/test/deploymentconfig.yml new file mode 100755 index 000000000..d041ab22a --- /dev/null +++ b/roles/lib_openshift_api/build/test/deploymentconfig.yml @@ -0,0 +1,120 @@ +#!/usr/bin/ansible-playbook +--- +- hosts: "oo_clusterid_mwoodson:&oo_version_3:&oo_master_primary" + gather_facts: no + user: root + + post_tasks: + - copy: + dest: "/tmp/{{ item }}" + src: "files/{{ item }}" + with_items: + - dc.yml + + - name: list dc + oc_obj: + kind: dc + state: list + namespace: default + name: router + register: dcout + + - debug: + var: dcout + + - name: absent dc + oc_obj: + kind: dc + state: absent + namespace: default + name: router + register: dcout + + - debug: + var: dcout + + - name: present dc + oc_obj: + kind: dc + state: present + namespace: default + name: router + files: + - /tmp/dc.yml + register: dcout + + - debug: + var: dcout + + - name: dump router + oc_obj: + kind: dc + state: list + name: router + register: routerout + + - name: write router file + copy: + dest: /tmp/dc-mod.json + content: "{{ routerout.results[0] }}" + + - command: cat /tmp/dc-mod.json + register: catout + + - debug: + msg: "{{ catout }}" + + - command: "sed -i 's/: 80/: 81/g' /tmp/dc-mod.json" + register: catout + + - name: present dc update + oc_obj: + kind: dc + state: present + namespace: default + name: router + files: + - /tmp/dc-mod.json + delete_after: True + register: dcout + + - debug: + var: dcout + + - include_vars: "files/dc-mod.yml" + + - name: absent dc + oc_obj: + kind: dc + state: absent + namespace: default + name: router + register: dcout + + - debug: + var: dcout + + - name: present dc + oc_obj: + kind: dc + state: present + namespace: default + name: router + files: + - /tmp/dc.yml + delete_after: True + register: dcout + + - name: present dc + oc_obj: + kind: dc + state: present + namespace: default + name: router + content: "{{ dc }}" + delete_after: True + register: dcout + + - debug: + var: dcout + diff --git a/roles/lib_openshift_api/build/test/edit.yml b/roles/lib_openshift_api/build/test/edit.yml new file mode 100755 index 000000000..9aa01303a --- /dev/null +++ b/roles/lib_openshift_api/build/test/edit.yml @@ -0,0 +1,53 @@ +#!/usr/bin/ansible-playbook +--- +- hosts: "oo_clusterid_mwoodson:&oo_version_3:&oo_master_primary" + gather_facts: no + user: root + + post_tasks: + - copy: + dest: "/tmp/{{ item }}" + src: "files/{{ item }}" + with_items: + - dc.yml + + - name: present dc + oc_edit: + kind: dc + namespace: default + name: router + content: + spec.template.spec.containers[0].ports[0].containerPort: 80 + spec.template.spec.containers[0].ports[0].hostPort: 80 + register: dcout + + - debug: + var: dcout + + - name: present dc + oc_edit: + kind: dc + namespace: default + name: router + content: + spec.template.spec.containers[0].ports[0].containerPort: 81 + spec.template.spec.containers[0].ports[0].hostPort: 81 + file_format: yaml + register: dcout + + - debug: + var: dcout + + - name: present dc + oc_edit: + kind: dc + namespace: default + name: router + content: + spec.template.spec.containers[0].ports[0].containerPort: 80 + spec.template.spec.containers[0].ports[0].hostPort: 80 + file_format: yaml + register: dcout + + - debug: + var: dcout diff --git a/roles/lib_openshift_api/build/test/files/config.yml b/roles/lib_openshift_api/build/test/files/config.yml new file mode 100644 index 000000000..c544c6fd4 --- /dev/null +++ b/roles/lib_openshift_api/build/test/files/config.yml @@ -0,0 +1 @@ +value: True diff --git a/roles/lib_openshift_api/build/test/files/dc-mod.yml b/roles/lib_openshift_api/build/test/files/dc-mod.yml new file mode 100644 index 000000000..6c700d6c7 --- /dev/null +++ b/roles/lib_openshift_api/build/test/files/dc-mod.yml @@ -0,0 +1,124 @@ +dc: + path: + dc-mod.yml + content: + apiVersion: v1 + kind: DeploymentConfig + metadata: + labels: + router: router + name: router + namespace: default + resourceVersion: "84016" + selfLink: /oapi/v1/namespaces/default/deploymentconfigs/router + uid: 48f8b9d9-ed42-11e5-9903-0a9a9d4e7f2b + spec: + replicas: 2 + selector: + router: router + strategy: + resources: {} + rollingParams: + intervalSeconds: 1 + maxSurge: 0 + maxUnavailable: 25% + timeoutSeconds: 600 + updatePercent: -25 + updatePeriodSeconds: 1 + type: Rolling + template: + metadata: + creationTimestamp: null + labels: + router: router + spec: + containers: + - env: + - name: DEFAULT_CERTIFICATE + - name: OPENSHIFT_CA_DATA + value: | + -----BEGIN CERTIFICATE----- + MIIC5jCCAdCgAwIBAgIBATALBgkqhkiG9w0BAQswJjEkMCIGA1UEAwwbb3BlbnNo + -----END CERTIFICATE----- + - name: OPENSHIFT_CERT_DATA + value: | + -----BEGIN CERTIFICATE----- + MIIDDTCCAfegAwIBAgIBCDALBgkqhkiG9w0BAQswJjEkMCIGA1UEAwwbb3BlbnNo + -----END CERTIFICATE----- + - name: OPENSHIFT_INSECURE + value: "false" + - name: OPENSHIFT_KEY_DATA + value: | + -----BEGIN RSA PRIVATE KEY----- + MIIEogIBAAKCAQEA2lf49DrPHfCdCORcnIbmDVrx8yos7trjWdBvuledijyslRVR + -----END RSA PRIVATE KEY----- + - name: OPENSHIFT_MASTER + value: https://internal.api.mwoodson.openshift.com + - name: ROUTER_EXTERNAL_HOST_HOSTNAME + - name: ROUTER_EXTERNAL_HOST_HTTPS_VSERVER + - name: ROUTER_EXTERNAL_HOST_HTTP_VSERVER + - name: ROUTER_EXTERNAL_HOST_INSECURE + value: "false" + - name: ROUTER_EXTERNAL_HOST_PARTITION_PATH + - name: ROUTER_EXTERNAL_HOST_PASSWORD + - name: ROUTER_EXTERNAL_HOST_PRIVKEY + value: /etc/secret-volume/router.pem + - name: ROUTER_EXTERNAL_HOST_USERNAME + - name: ROUTER_SERVICE_NAME + value: router + - name: ROUTER_SERVICE_NAMESPACE + value: default + - name: STATS_PASSWORD + value: ugCk6YBm4q + - name: STATS_PORT + value: "1936" + - name: STATS_USERNAME + value: admin + image: openshift3/ose-haproxy-router:v3.1.1.6 + imagePullPolicy: IfNotPresent + livenessProbe: + httpGet: + host: localhost + path: /healthz + port: 1936 + scheme: HTTP + initialDelaySeconds: 10 + timeoutSeconds: 1 + name: router + ports: + - containerPort: 81 + hostPort: 81 + protocol: TCP + - containerPort: 443 + hostPort: 443 + protocol: TCP + - containerPort: 1936 + hostPort: 1936 + name: stats + protocol: TCP + readinessProbe: + httpGet: + host: localhost + path: /healthz + port: 1937 + scheme: HTTP + timeoutSeconds: 1 + resources: {} + terminationMessagePath: /dev/termination-log + dnsPolicy: ClusterFirst + hostNetwork: true + nodeSelector: + type: infra + restartPolicy: Always + securityContext: {} + serviceAccount: router + serviceAccountName: router + terminationGracePeriodSeconds: 30 + triggers: + - type: ConfigChange + status: + details: + causes: + - type: ConfigChange + latestVersion: 1 + diff --git a/roles/lib_openshift_api/build/test/files/dc.yml b/roles/lib_openshift_api/build/test/files/dc.yml new file mode 100644 index 000000000..24f690ef4 --- /dev/null +++ b/roles/lib_openshift_api/build/test/files/dc.yml @@ -0,0 +1,120 @@ +apiVersion: v1 +kind: DeploymentConfig +metadata: + creationTimestamp: 2016-04-01T15:23:29Z + labels: + router: router + name: router + namespace: default + resourceVersion: "1338477" + selfLink: /oapi/v1/namespaces/default/deploymentconfigs/router + uid: b00c7eba-f81d-11e5-809b-0a581f893e3f +spec: + replicas: 2 + selector: + router: router + strategy: + resources: {} + rollingParams: + intervalSeconds: 1 + maxSurge: 0 + maxUnavailable: 25% + timeoutSeconds: 600 + updatePercent: -25 + updatePeriodSeconds: 1 + type: Rolling + template: + metadata: + creationTimestamp: null + labels: + router: router + spec: + containers: + - env: + - name: DEFAULT_CERTIFICATE + - name: OPENSHIFT_CA_DATA + value: | + -----BEGIN CERTIFICATE----- + MIIC5jCCAdCgAwIBAgIBATALBgkqhkiG9w0BAQswJjEkMCIGA1UEAwwbb3BlbnNo + -----END CERTIFICATE----- + - name: OPENSHIFT_CERT_DATA + value: | + -----BEGIN CERTIFICATE----- + MIIDDTCCAfegAwIBAgIBCDALBgkqhkiG9w0BAQswJjEkMCIGA1UEAwwbb3BlbnNo + -----END CERTIFICATE----- + - name: OPENSHIFT_INSECURE + value: "false" + - name: OPENSHIFT_KEY_DATA + value: | + -----BEGIN RSA PRIVATE KEY----- + MIIEogIBAAKCAQEA2lf49DrPHfCdCORcnIbmDVrx8yos7trjWdBvuledijyslRVR + -----END RSA PRIVATE KEY----- + - name: OPENSHIFT_MASTER + value: https://internal.api.mwoodson.openshift.com + - name: ROUTER_EXTERNAL_HOST_HOSTNAME + - name: ROUTER_EXTERNAL_HOST_HTTPS_VSERVER + - name: ROUTER_EXTERNAL_HOST_HTTP_VSERVER + - name: ROUTER_EXTERNAL_HOST_INSECURE + value: "false" + - name: ROUTER_EXTERNAL_HOST_PARTITION_PATH + - name: ROUTER_EXTERNAL_HOST_PASSWORD + - name: ROUTER_EXTERNAL_HOST_PRIVKEY + value: /etc/secret-volume/router.pem + - name: ROUTER_EXTERNAL_HOST_USERNAME + - name: ROUTER_SERVICE_NAME + value: router + - name: ROUTER_SERVICE_NAMESPACE + value: default + - name: STATS_PASSWORD + value: ugCk6YBm4q + - name: STATS_PORT + value: "1936" + - name: STATS_USERNAME + value: admin + image: openshift3/ose-haproxy-router:v3.1.1.6 + imagePullPolicy: IfNotPresent + livenessProbe: + httpGet: + host: localhost + path: /healthz + port: 1936 + scheme: HTTP + initialDelaySeconds: 10 + timeoutSeconds: 1 + name: router + ports: + - containerPort: 80 + hostPort: 80 + protocol: TCP + - containerPort: 443 + hostPort: 443 + protocol: TCP + - containerPort: 1936 + hostPort: 1936 + name: stats + protocol: TCP + readinessProbe: + httpGet: + host: localhost + path: /healthz + port: 1936 + scheme: HTTP + timeoutSeconds: 1 + resources: {} + terminationMessagePath: /dev/termination-log + dnsPolicy: ClusterFirst + hostNetwork: true + nodeSelector: + type: infra + restartPolicy: Always + securityContext: {} + serviceAccount: router + serviceAccountName: router + terminationGracePeriodSeconds: 30 + triggers: + - type: ConfigChange +status: + details: + causes: + - type: ConfigChange + latestVersion: 12 diff --git a/roles/lib_openshift_api/build/test/files/passwords.yml b/roles/lib_openshift_api/build/test/files/passwords.yml new file mode 100644 index 000000000..fadbf1d85 --- /dev/null +++ b/roles/lib_openshift_api/build/test/files/passwords.yml @@ -0,0 +1,4 @@ +test1 +test2 +test3 +test4 diff --git a/roles/lib_openshift_api/build/test/files/router-mod.json b/roles/lib_openshift_api/build/test/files/router-mod.json new file mode 100644 index 000000000..45e2e7c8d --- /dev/null +++ b/roles/lib_openshift_api/build/test/files/router-mod.json @@ -0,0 +1,30 @@ +{ + "kind": "Service", + "apiVersion": "v1", + "metadata": { + "name": "router", + "namespace": "default", + "labels": { + "router": "router" + } + }, + "spec": { + "ports": [ + { + "name": "81-tcp", + "protocol": "TCP", + "port": 81, + "targetPort": 81 + } + ], + "selector": { + "router": "router" + }, + "type": "ClusterIP", + "sessionAffinity": "None" + }, + "status": { + "loadBalancer": {} + } +} + diff --git a/roles/lib_openshift_api/build/test/files/router.json b/roles/lib_openshift_api/build/test/files/router.json new file mode 100644 index 000000000..cad3c6f53 --- /dev/null +++ b/roles/lib_openshift_api/build/test/files/router.json @@ -0,0 +1,29 @@ +{ + "apiVersion": "v1", + "kind": "Service", + "metadata": { + "labels": { + "router": "router" + }, + "name": "router", + "namespace": "default" + }, + "spec": { + "ports": [ + { + "name": "80-tcp", + "port": 80, + "protocol": "TCP", + "targetPort": 80 + } + ], + "selector": { + "router": "router" + }, + "sessionAffinity": "None", + "type": "ClusterIP" + }, + "status": { + "loadBalancer": {} + } +} diff --git a/roles/lib_openshift_api/build/test/roles b/roles/lib_openshift_api/build/test/roles new file mode 120000 index 000000000..ae82aa9bb --- /dev/null +++ b/roles/lib_openshift_api/build/test/roles @@ -0,0 +1 @@ +../../../../roles/
\ No newline at end of file diff --git a/roles/lib_openshift_api/build/test/secrets.yml b/roles/lib_openshift_api/build/test/secrets.yml new file mode 100755 index 000000000..dddc05c4d --- /dev/null +++ b/roles/lib_openshift_api/build/test/secrets.yml @@ -0,0 +1,81 @@ +#!/usr/bin/ansible-playbook +--- +- hosts: "oo_clusterid_mwoodson:&oo_version_3:&oo_master_primary" + gather_facts: no + user: root + + post_tasks: + - copy: + dest: "/tmp/{{ item }}" + src: "files/{{ item }}" + with_items: + - config.yml + - passwords.yml + + - name: list secrets + oc_secret: + state: list + namespace: default + name: kenny + register: secret_out + + - debug: + var: secret_out + + - name: absent secrets + oc_secret: + state: absent + namespace: default + name: kenny + register: secret_out + + - debug: + var: secret_out + + - name: present secrets + oc_secret: + state: present + namespace: default + name: kenny + files: + - /tmp/config.yml + - /tmp/passwords.yml + delete_after: True + register: secret_out + + - debug: + var: secret_out + + - name: present secrets + oc_secret: + state: present + namespace: default + name: kenny + contents: + - path: config.yml + content: "value: True\n" + - path: passwords.yml + content: "test1\ntest2\ntest3\ntest4\n" + delete_after: True + register: secret_out + + - debug: + var: secret_out + + - name: present secrets update + oc_secret: + state: present + namespace: default + name: kenny + contents: + - path: config.yml + content: "value: True\n" + - path: passwords.yml + content: "test1\ntest2\ntest3\ntest4\ntest5\n" + delete_after: True + force: True + register: secret_out + + - debug: + var: secret_out + diff --git a/roles/lib_openshift_api/build/test/services.yml b/roles/lib_openshift_api/build/test/services.yml new file mode 100755 index 000000000..a32e8d012 --- /dev/null +++ b/roles/lib_openshift_api/build/test/services.yml @@ -0,0 +1,133 @@ +#!/usr/bin/ansible-playbook +--- +- hosts: "oo_clusterid_mwoodson:&oo_master_primary" + gather_facts: no + user: root + + roles: + - roles/lib_yaml_editor + + tasks: + - copy: + dest: "/tmp/{{ item }}" + src: "files/{{ item }}" + with_items: + - router.json + - router-mod.json + + - name: list services + oc_obj: + kind: service + state: list + namespace: default + name: router + register: service_out + + - debug: + var: service_out.results + + - name: absent service + oc_obj: + kind: service + state: absent + namespace: default + name: router + register: service_out + + - debug: + var: service_out + + - name: present service create + oc_obj: + kind: service + state: present + namespace: default + name: router + files: + - /tmp/router.json + delete_after: True + register: service_out + + - debug: + var: service_out + + - name: dump router + oc_obj: + kind: service + state: list + name: router + namespace: default + register: routerout + + - name: write router file + copy: + dest: /tmp/router-mod.json + content: "{{ routerout.results[0] }}" + + - command: cat /tmp/router-mod.json + register: catout + + - debug: + msg: "{{ catout }}" + + - command: "sed -i 's/80-tcp/81-tcp/g' /tmp/router-mod.json" + register: catout + + - name: present service replace + oc_obj: + kind: service + state: present + namespace: default + name: router + files: + - /tmp/router-mod.json + #delete_after: True + register: service_out + + - debug: + var: service_out + + - name: list services + oc_obj: + kind: service + state: list + namespace: default + name: router + register: service_out + + - debug: + var: service_out.results + + - set_fact: + new_service: "{{ service_out.results[0] }}" + + - yedit: + src: /tmp/routeryedit + content: "{{ new_service }}" + key: spec.ports + value: + - name: 80-tcp + port: 80 + protocol: TCP + targetPort: 80 + + - yedit: + src: /tmp/routeryedit + state: list + register: yeditout + + - debug: + var: yeditout + + - name: present service replace + oc_obj: + kind: service + state: present + namespace: default + name: router + content: "{{ yeditout.results }}" + delete_after: True + register: service_out + + - debug: + var: service_out diff --git a/roles/lib_openshift_api/library/oc_edit.py b/roles/lib_openshift_api/library/oc_edit.py new file mode 100644 index 000000000..44e77331d --- /dev/null +++ b/roles/lib_openshift_api/library/oc_edit.py @@ -0,0 +1,619 @@ +#!/usr/bin/env python +# ___ ___ _ _ ___ ___ _ _____ ___ ___ +# / __| __| \| | __| _ \ /_\_ _| __| \ +# | (_ | _|| .` | _|| / / _ \| | | _|| |) | +# \___|___|_|\_|___|_|_\/_/_\_\_|_|___|___/_ _____ +# | \ / _ \ | \| |/ _ \_ _| | __| \_ _|_ _| +# | |) | (_) | | .` | (_) || | | _|| |) | | | | +# |___/ \___/ |_|\_|\___/ |_| |___|___/___| |_| +''' + OpenShiftCLI class that wraps the oc commands in a subprocess +''' + +import atexit +import json +import os +import shutil +import subprocess +import re + +import yaml +# This is here because of a bug that causes yaml +# to incorrectly handle timezone info on timestamps +def timestamp_constructor(_, node): + '''return timestamps as strings''' + return str(node.value) +yaml.add_constructor(u'tag:yaml.org,2002:timestamp', timestamp_constructor) + +# pylint: disable=too-few-public-methods +class OpenShiftCLI(object): + ''' Class to wrap the oc command line tools ''' + def __init__(self, + namespace, + kubeconfig='/etc/origin/master/admin.kubeconfig', + verbose=False): + ''' Constructor for OpenshiftOC ''' + self.namespace = namespace + self.verbose = verbose + self.kubeconfig = kubeconfig + + # Pylint allows only 5 arguments to be passed. + # pylint: disable=too-many-arguments + def _replace_content(self, resource, rname, content, force=False): + ''' replace the current object with the content ''' + res = self._get(resource, rname) + if not res['results']: + return res + + fname = '/tmp/%s' % rname + yed = Yedit(fname, res['results'][0]) + changes = [] + for key, value in content.items(): + changes.append(yed.put(key, value)) + + if any([not change[0] for change in changes]): + return {'returncode': 0, 'updated': False} + + yed.write() + + atexit.register(Utils.cleanup, [fname]) + + return self._replace(fname, force) + + def _replace(self, fname, force=False): + '''return all pods ''' + cmd = ['-n', self.namespace, 'replace', '-f', fname] + if force: + cmd.append('--force') + return self.oc_cmd(cmd) + + def _create(self, fname): + '''return all pods ''' + return self.oc_cmd(['create', '-f', fname, '-n', self.namespace]) + + def _delete(self, resource, rname): + '''return all pods ''' + return self.oc_cmd(['delete', resource, rname, '-n', self.namespace]) + + def _get(self, resource, rname=None): + '''return a secret by name ''' + cmd = ['get', resource, '-o', 'json', '-n', self.namespace] + if rname: + cmd.append(rname) + + rval = self.oc_cmd(cmd, output=True) + + # Ensure results are retuned in an array + if rval.has_key('items'): + rval['results'] = rval['items'] + elif not isinstance(rval['results'], list): + rval['results'] = [rval['results']] + + return rval + + def oc_cmd(self, cmd, output=False): + '''Base command for oc ''' + #cmds = ['/usr/bin/oc', '--config', self.kubeconfig] + cmds = ['/usr/bin/oc'] + cmds.extend(cmd) + + rval = {} + results = '' + err = None + + if self.verbose: + print ' '.join(cmds) + + proc = subprocess.Popen(cmds, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env={'KUBECONFIG': self.kubeconfig}) + + proc.wait() + stdout = proc.stdout.read() + stderr = proc.stderr.read() + + rval = {"returncode": proc.returncode, + "results": results, + } + + if proc.returncode == 0: + if output: + try: + rval['results'] = json.loads(stdout) + except ValueError as err: + if "No JSON object could be decoded" in err.message: + err = err.message + + if self.verbose: + print stdout + print stderr + print + + if err: + rval.update({"err": err, + "stderr": stderr, + "stdout": stdout, + "cmd": cmds + }) + + else: + rval.update({"stderr": stderr, + "stdout": stdout, + "results": {}, + }) + + return rval + +class Utils(object): + ''' utilities for openshiftcli modules ''' + @staticmethod + def create_file(rname, data, ftype=None): + ''' create a file in tmp with name and contents''' + path = os.path.join('/tmp', rname) + with open(path, 'w') as fds: + if ftype == 'yaml': + fds.write(yaml.safe_dump(data, default_flow_style=False)) + + elif ftype == 'json': + fds.write(json.dumps(data)) + else: + fds.write(data) + + # Register cleanup when module is done + atexit.register(Utils.cleanup, [path]) + return path + + @staticmethod + def create_files_from_contents(data): + '''Turn an array of dict: filename, content into a files array''' + files = [] + + for sfile in data: + path = Utils.create_file(sfile['path'], sfile['content']) + files.append(path) + + return files + + @staticmethod + def cleanup(files): + '''Clean up on exit ''' + for sfile in files: + if os.path.exists(sfile): + if os.path.isdir(sfile): + shutil.rmtree(sfile) + elif os.path.isfile(sfile): + os.remove(sfile) + + + @staticmethod + def exists(results, _name): + ''' Check to see if the results include the name ''' + if not results: + return False + + + if Utils.find_result(results, _name): + return True + + return False + + @staticmethod + def find_result(results, _name): + ''' Find the specified result by name''' + rval = None + for result in results: + if result.has_key('metadata') and result['metadata']['name'] == _name: + rval = result + break + + return rval + + @staticmethod + def get_resource_file(sfile, sfile_type='yaml'): + ''' return the service file ''' + contents = None + with open(sfile) as sfd: + contents = sfd.read() + + if sfile_type == 'yaml': + contents = yaml.safe_load(contents) + elif sfile_type == 'json': + contents = json.loads(contents) + + return contents + + # Disabling too-many-branches. This is a yaml dictionary comparison function + # pylint: disable=too-many-branches,too-many-return-statements + @staticmethod + def check_def_equal(user_def, result_def, debug=False): + ''' Given a user defined definition, compare it with the results given back by our query. ''' + + # Currently these values are autogenerated and we do not need to check them + skip = ['metadata', 'status'] + + for key, value in result_def.items(): + if key in skip: + continue + + # Both are lists + if isinstance(value, list): + if not isinstance(user_def[key], list): + return False + + # lists should be identical + if value != user_def[key]: + return False + + # recurse on a dictionary + elif isinstance(value, dict): + if not isinstance(user_def[key], dict): + if debug: + print "dict returned false not instance of dict" + return False + + # before passing ensure keys match + api_values = set(value.keys()) - set(skip) + user_values = set(user_def[key].keys()) - set(skip) + if api_values != user_values: + if debug: + print api_values + print user_values + print "keys are not equal in dict" + return False + + result = Utils.check_def_equal(user_def[key], value, debug=debug) + if not result: + if debug: + print "dict returned false" + return False + + # Verify each key, value pair is the same + else: + if not user_def.has_key(key) or value != user_def[key]: + if debug: + print "value not equal; user_def does not have key" + print value + print user_def[key] + return False + + return True + +class YeditException(Exception): + ''' Exception class for Yedit ''' + pass + +class Yedit(object): + ''' Class to modify yaml files ''' + re_valid_key = r"(((\[-?\d+\])|([a-zA-Z-./]+)).?)+$" + re_key = r"(?:\[(-?\d+)\])|([a-zA-Z-./]+)" + + def __init__(self, filename=None, content=None, content_type='yaml'): + self.content = content + self.filename = filename + self.__yaml_dict = content + self.content_type = content_type + if self.filename and not self.content: + self.load(content_type=self.content_type) + + @property + def yaml_dict(self): + ''' getter method for yaml_dict ''' + return self.__yaml_dict + + @yaml_dict.setter + def yaml_dict(self, value): + ''' setter method for yaml_dict ''' + self.__yaml_dict = value + + @staticmethod + def remove_entry(data, key): + ''' remove data at location key ''' + if not (key and re.match(Yedit.re_valid_key, key) and isinstance(data, (list, dict))): + return None + + key_indexes = re.findall(Yedit.re_key, key) + for arr_ind, dict_key in key_indexes[:-1]: + if dict_key and isinstance(data, dict): + data = data.get(dict_key, None) + elif arr_ind and isinstance(data, list) and int(arr_ind) <= len(data) - 1: + data = data[int(arr_ind)] + else: + return None + + # process last index for remove + # expected list entry + if key_indexes[-1][0]: + if isinstance(data, list) and int(key_indexes[-1][0]) <= len(data) - 1: + del data[int(key_indexes[-1][0])] + return True + + # expected dict entry + elif key_indexes[-1][1]: + if isinstance(data, dict): + del data[key_indexes[-1][1]] + return True + + @staticmethod + def add_entry(data, key, item=None): + ''' Get an item from a dictionary with key notation a.b.c + d = {'a': {'b': 'c'}}} + key = a.b + return c + ''' + if not (key and re.match(Yedit.re_valid_key, key) and isinstance(data, (list, dict))): + return None + + curr_data = data + + key_indexes = re.findall(Yedit.re_key, key) + for arr_ind, dict_key in key_indexes[:-1]: + if dict_key: + if isinstance(data, dict) and data.has_key(dict_key): + data = data[dict_key] + continue + + data[dict_key] = {} + data = data[dict_key] + + elif arr_ind and isinstance(data, list) and int(arr_ind) <= len(data) - 1: + data = data[int(arr_ind)] + else: + return None + + # process last index for add + # expected list entry + if key_indexes[-1][0] and isinstance(data, list) and int(key_indexes[-1][0]) <= len(data) - 1: + data[int(key_indexes[-1][0])] = item + + # expected dict entry + elif key_indexes[-1][1] and isinstance(data, dict): + data[key_indexes[-1][1]] = item + + return curr_data + + @staticmethod + def get_entry(data, key): + ''' Get an item from a dictionary with key notation a.b.c + d = {'a': {'b': 'c'}}} + key = a.b + return c + ''' + if not (key and re.match(Yedit.re_valid_key, key) and isinstance(data, (list, dict))): + return None + + key_indexes = re.findall(Yedit.re_key, key) + for arr_ind, dict_key in key_indexes: + if dict_key and isinstance(data, dict): + data = data.get(dict_key, None) + elif arr_ind and isinstance(data, list) and int(arr_ind) <= len(data) - 1: + data = data[int(arr_ind)] + else: + return None + + return data + + def write(self): + ''' write to file ''' + if not self.filename: + raise YeditException('Please specify a filename.') + + with open(self.filename, 'w') as yfd: + yfd.write(yaml.safe_dump(self.yaml_dict, default_flow_style=False)) + + def read(self): + ''' write to file ''' + # check if it exists + if not self.exists(): + return None + + contents = None + with open(self.filename) as yfd: + contents = yfd.read() + + return contents + + def exists(self): + ''' return whether file exists ''' + if os.path.exists(self.filename): + return True + + return False + + def load(self, content_type='yaml'): + ''' return yaml file ''' + contents = self.read() + + if not contents: + return None + + # check if it is yaml + try: + if content_type == 'yaml': + self.yaml_dict = yaml.load(contents) + elif content_type == 'json': + self.yaml_dict = json.loads(contents) + except yaml.YAMLError as _: + # Error loading yaml or json + return None + + return self.yaml_dict + + def get(self, key): + ''' get a specified key''' + try: + entry = Yedit.get_entry(self.yaml_dict, key) + except KeyError as _: + entry = None + + return entry + + def delete(self, key): + ''' remove key from a dict''' + try: + entry = Yedit.get_entry(self.yaml_dict, key) + except KeyError as _: + entry = None + if not entry: + return (False, self.yaml_dict) + + result = Yedit.remove_entry(self.yaml_dict, key) + if not result: + return (False, self.yaml_dict) + + return (True, self.yaml_dict) + + def put(self, key, value): + ''' put key, value into a dict ''' + try: + entry = Yedit.get_entry(self.yaml_dict, key) + except KeyError as _: + entry = None + + if entry == value: + return (False, self.yaml_dict) + + result = Yedit.add_entry(self.yaml_dict, key, value) + if not result: + return (False, self.yaml_dict) + + return (True, self.yaml_dict) + + def create(self, key, value): + ''' create a yaml file ''' + if not self.exists(): + self.yaml_dict = {key: value} + return (True, self.yaml_dict) + + return (False, self.yaml_dict) + +class Edit(OpenShiftCLI): + ''' Class to wrap the oc command line tools + ''' + # pylint: disable=too-many-arguments + def __init__(self, + kind, + namespace, + resource_name=None, + kubeconfig='/etc/origin/master/admin.kubeconfig', + verbose=False): + ''' Constructor for OpenshiftOC ''' + super(Edit, self).__init__(namespace, kubeconfig) + self.namespace = namespace + self.kind = kind + self.name = resource_name + self.kubeconfig = kubeconfig + self.verbose = verbose + + def get(self): + '''return a secret by name ''' + return self._get(self.kind, self.name) + + def update(self, file_name, content, force=False, content_type='yaml'): + '''run update ''' + if file_name: + if content_type == 'yaml': + data = yaml.load(open(file_name)) + elif content_type == 'json': + data = json.loads(open(file_name).read()) + + changes = [] + yed = Yedit(file_name, data) + for key, value in content.items(): + changes.append(yed.put(key, value)) + + if any([not change[0] for change in changes]): + return {'returncode': 0, 'updated': False} + + yed.write() + + atexit.register(Utils.cleanup, [file_name]) + + return self._replace(file_name, force=force) + + return self._replace_content(self.kind, self.name, content, force=force) + + + +def main(): + ''' + ansible oc module for services + ''' + + module = AnsibleModule( + argument_spec=dict( + kubeconfig=dict(default='/etc/origin/master/admin.kubeconfig', type='str'), + state=dict(default='present', type='str', + choices=['present']), + debug=dict(default=False, type='bool'), + namespace=dict(default='default', type='str'), + name=dict(default=None, required=True, type='str'), + kind=dict(required=True, + type='str', + choices=['dc', 'deploymentconfig', + 'svc', 'service', + 'scc', 'securitycontextconstraints', + 'ns', 'namespace', 'project', 'projects', + 'is', 'imagestream', + 'istag', 'imagestreamtag', + 'bc', 'buildconfig', + 'routes', + 'node', + 'secret', + ]), + file_name=dict(default=None, type='str'), + file_format=dict(default='yaml', type='str'), + content=dict(default=None, required=True, type='dict'), + force=dict(default=False, type='bool'), + ), + supports_check_mode=True, + ) + ocedit = Edit(module.params['kind'], + module.params['namespace'], + module.params['name'], + kubeconfig=module.params['kubeconfig'], + verbose=module.params['debug']) + + state = module.params['state'] + + api_rval = ocedit.get() + + ######## + # Create + ######## + if not Utils.exists(api_rval['results'], module.params['name']): + module.fail_json(msg=api_rval) + + ######## + # Update + ######## + api_rval = ocedit.update(module.params['file_name'], + module.params['content'], + module.params['force'], + module.params['file_format']) + + + if api_rval['returncode'] != 0: + module.fail_json(msg=api_rval) + + if api_rval.has_key('updated') and not api_rval['updated']: + module.exit_json(changed=False, results=api_rval, state="present") + + # return the created object + api_rval = ocedit.get() + + if api_rval['returncode'] != 0: + module.fail_json(msg=api_rval) + + module.exit_json(changed=True, results=api_rval, state="present") + + module.exit_json(failed=True, + changed=False, + results='Unknown state passed. %s' % state, + state="unknown") + +# pylint: disable=redefined-builtin, unused-wildcard-import, wildcard-import, locally-disabled +# import module snippets. This are required +from ansible.module_utils.basic import * + +main() diff --git a/roles/lib_openshift_api/library/oc_obj.py b/roles/lib_openshift_api/library/oc_obj.py new file mode 100644 index 000000000..c058072e3 --- /dev/null +++ b/roles/lib_openshift_api/library/oc_obj.py @@ -0,0 +1,703 @@ +#!/usr/bin/env python +# ___ ___ _ _ ___ ___ _ _____ ___ ___ +# / __| __| \| | __| _ \ /_\_ _| __| \ +# | (_ | _|| .` | _|| / / _ \| | | _|| |) | +# \___|___|_|\_|___|_|_\/_/_\_\_|_|___|___/_ _____ +# | \ / _ \ | \| |/ _ \_ _| | __| \_ _|_ _| +# | |) | (_) | | .` | (_) || | | _|| |) | | | | +# |___/ \___/ |_|\_|\___/ |_| |___|___/___| |_| +''' + OpenShiftCLI class that wraps the oc commands in a subprocess +''' + +import atexit +import json +import os +import shutil +import subprocess +import re + +import yaml +# This is here because of a bug that causes yaml +# to incorrectly handle timezone info on timestamps +def timestamp_constructor(_, node): + '''return timestamps as strings''' + return str(node.value) +yaml.add_constructor(u'tag:yaml.org,2002:timestamp', timestamp_constructor) + +# pylint: disable=too-few-public-methods +class OpenShiftCLI(object): + ''' Class to wrap the oc command line tools ''' + def __init__(self, + namespace, + kubeconfig='/etc/origin/master/admin.kubeconfig', + verbose=False): + ''' Constructor for OpenshiftOC ''' + self.namespace = namespace + self.verbose = verbose + self.kubeconfig = kubeconfig + + # Pylint allows only 5 arguments to be passed. + # pylint: disable=too-many-arguments + def _replace_content(self, resource, rname, content, force=False): + ''' replace the current object with the content ''' + res = self._get(resource, rname) + if not res['results']: + return res + + fname = '/tmp/%s' % rname + yed = Yedit(fname, res['results'][0]) + changes = [] + for key, value in content.items(): + changes.append(yed.put(key, value)) + + if any([not change[0] for change in changes]): + return {'returncode': 0, 'updated': False} + + yed.write() + + atexit.register(Utils.cleanup, [fname]) + + return self._replace(fname, force) + + def _replace(self, fname, force=False): + '''return all pods ''' + cmd = ['-n', self.namespace, 'replace', '-f', fname] + if force: + cmd.append('--force') + return self.oc_cmd(cmd) + + def _create(self, fname): + '''return all pods ''' + return self.oc_cmd(['create', '-f', fname, '-n', self.namespace]) + + def _delete(self, resource, rname): + '''return all pods ''' + return self.oc_cmd(['delete', resource, rname, '-n', self.namespace]) + + def _get(self, resource, rname=None): + '''return a secret by name ''' + cmd = ['get', resource, '-o', 'json', '-n', self.namespace] + if rname: + cmd.append(rname) + + rval = self.oc_cmd(cmd, output=True) + + # Ensure results are retuned in an array + if rval.has_key('items'): + rval['results'] = rval['items'] + elif not isinstance(rval['results'], list): + rval['results'] = [rval['results']] + + return rval + + def oc_cmd(self, cmd, output=False): + '''Base command for oc ''' + #cmds = ['/usr/bin/oc', '--config', self.kubeconfig] + cmds = ['/usr/bin/oc'] + cmds.extend(cmd) + + rval = {} + results = '' + err = None + + if self.verbose: + print ' '.join(cmds) + + proc = subprocess.Popen(cmds, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env={'KUBECONFIG': self.kubeconfig}) + + proc.wait() + stdout = proc.stdout.read() + stderr = proc.stderr.read() + + rval = {"returncode": proc.returncode, + "results": results, + } + + if proc.returncode == 0: + if output: + try: + rval['results'] = json.loads(stdout) + except ValueError as err: + if "No JSON object could be decoded" in err.message: + err = err.message + + if self.verbose: + print stdout + print stderr + print + + if err: + rval.update({"err": err, + "stderr": stderr, + "stdout": stdout, + "cmd": cmds + }) + + else: + rval.update({"stderr": stderr, + "stdout": stdout, + "results": {}, + }) + + return rval + +class Utils(object): + ''' utilities for openshiftcli modules ''' + @staticmethod + def create_file(rname, data, ftype=None): + ''' create a file in tmp with name and contents''' + path = os.path.join('/tmp', rname) + with open(path, 'w') as fds: + if ftype == 'yaml': + fds.write(yaml.safe_dump(data, default_flow_style=False)) + + elif ftype == 'json': + fds.write(json.dumps(data)) + else: + fds.write(data) + + # Register cleanup when module is done + atexit.register(Utils.cleanup, [path]) + return path + + @staticmethod + def create_files_from_contents(data): + '''Turn an array of dict: filename, content into a files array''' + files = [] + + for sfile in data: + path = Utils.create_file(sfile['path'], sfile['content']) + files.append(path) + + return files + + @staticmethod + def cleanup(files): + '''Clean up on exit ''' + for sfile in files: + if os.path.exists(sfile): + if os.path.isdir(sfile): + shutil.rmtree(sfile) + elif os.path.isfile(sfile): + os.remove(sfile) + + + @staticmethod + def exists(results, _name): + ''' Check to see if the results include the name ''' + if not results: + return False + + + if Utils.find_result(results, _name): + return True + + return False + + @staticmethod + def find_result(results, _name): + ''' Find the specified result by name''' + rval = None + for result in results: + if result.has_key('metadata') and result['metadata']['name'] == _name: + rval = result + break + + return rval + + @staticmethod + def get_resource_file(sfile, sfile_type='yaml'): + ''' return the service file ''' + contents = None + with open(sfile) as sfd: + contents = sfd.read() + + if sfile_type == 'yaml': + contents = yaml.safe_load(contents) + elif sfile_type == 'json': + contents = json.loads(contents) + + return contents + + # Disabling too-many-branches. This is a yaml dictionary comparison function + # pylint: disable=too-many-branches,too-many-return-statements + @staticmethod + def check_def_equal(user_def, result_def, debug=False): + ''' Given a user defined definition, compare it with the results given back by our query. ''' + + # Currently these values are autogenerated and we do not need to check them + skip = ['metadata', 'status'] + + for key, value in result_def.items(): + if key in skip: + continue + + # Both are lists + if isinstance(value, list): + if not isinstance(user_def[key], list): + return False + + # lists should be identical + if value != user_def[key]: + return False + + # recurse on a dictionary + elif isinstance(value, dict): + if not isinstance(user_def[key], dict): + if debug: + print "dict returned false not instance of dict" + return False + + # before passing ensure keys match + api_values = set(value.keys()) - set(skip) + user_values = set(user_def[key].keys()) - set(skip) + if api_values != user_values: + if debug: + print api_values + print user_values + print "keys are not equal in dict" + return False + + result = Utils.check_def_equal(user_def[key], value, debug=debug) + if not result: + if debug: + print "dict returned false" + return False + + # Verify each key, value pair is the same + else: + if not user_def.has_key(key) or value != user_def[key]: + if debug: + print "value not equal; user_def does not have key" + print value + print user_def[key] + return False + + return True + +class YeditException(Exception): + ''' Exception class for Yedit ''' + pass + +class Yedit(object): + ''' Class to modify yaml files ''' + re_valid_key = r"(((\[-?\d+\])|([a-zA-Z-./]+)).?)+$" + re_key = r"(?:\[(-?\d+)\])|([a-zA-Z-./]+)" + + def __init__(self, filename=None, content=None, content_type='yaml'): + self.content = content + self.filename = filename + self.__yaml_dict = content + self.content_type = content_type + if self.filename and not self.content: + self.load(content_type=self.content_type) + + @property + def yaml_dict(self): + ''' getter method for yaml_dict ''' + return self.__yaml_dict + + @yaml_dict.setter + def yaml_dict(self, value): + ''' setter method for yaml_dict ''' + self.__yaml_dict = value + + @staticmethod + def remove_entry(data, key): + ''' remove data at location key ''' + if not (key and re.match(Yedit.re_valid_key, key) and isinstance(data, (list, dict))): + return None + + key_indexes = re.findall(Yedit.re_key, key) + for arr_ind, dict_key in key_indexes[:-1]: + if dict_key and isinstance(data, dict): + data = data.get(dict_key, None) + elif arr_ind and isinstance(data, list) and int(arr_ind) <= len(data) - 1: + data = data[int(arr_ind)] + else: + return None + + # process last index for remove + # expected list entry + if key_indexes[-1][0]: + if isinstance(data, list) and int(key_indexes[-1][0]) <= len(data) - 1: + del data[int(key_indexes[-1][0])] + return True + + # expected dict entry + elif key_indexes[-1][1]: + if isinstance(data, dict): + del data[key_indexes[-1][1]] + return True + + @staticmethod + def add_entry(data, key, item=None): + ''' Get an item from a dictionary with key notation a.b.c + d = {'a': {'b': 'c'}}} + key = a.b + return c + ''' + if not (key and re.match(Yedit.re_valid_key, key) and isinstance(data, (list, dict))): + return None + + curr_data = data + + key_indexes = re.findall(Yedit.re_key, key) + for arr_ind, dict_key in key_indexes[:-1]: + if dict_key: + if isinstance(data, dict) and data.has_key(dict_key): + data = data[dict_key] + continue + + data[dict_key] = {} + data = data[dict_key] + + elif arr_ind and isinstance(data, list) and int(arr_ind) <= len(data) - 1: + data = data[int(arr_ind)] + else: + return None + + # process last index for add + # expected list entry + if key_indexes[-1][0] and isinstance(data, list) and int(key_indexes[-1][0]) <= len(data) - 1: + data[int(key_indexes[-1][0])] = item + + # expected dict entry + elif key_indexes[-1][1] and isinstance(data, dict): + data[key_indexes[-1][1]] = item + + return curr_data + + @staticmethod + def get_entry(data, key): + ''' Get an item from a dictionary with key notation a.b.c + d = {'a': {'b': 'c'}}} + key = a.b + return c + ''' + if not (key and re.match(Yedit.re_valid_key, key) and isinstance(data, (list, dict))): + return None + + key_indexes = re.findall(Yedit.re_key, key) + for arr_ind, dict_key in key_indexes: + if dict_key and isinstance(data, dict): + data = data.get(dict_key, None) + elif arr_ind and isinstance(data, list) and int(arr_ind) <= len(data) - 1: + data = data[int(arr_ind)] + else: + return None + + return data + + def write(self): + ''' write to file ''' + if not self.filename: + raise YeditException('Please specify a filename.') + + with open(self.filename, 'w') as yfd: + yfd.write(yaml.safe_dump(self.yaml_dict, default_flow_style=False)) + + def read(self): + ''' write to file ''' + # check if it exists + if not self.exists(): + return None + + contents = None + with open(self.filename) as yfd: + contents = yfd.read() + + return contents + + def exists(self): + ''' return whether file exists ''' + if os.path.exists(self.filename): + return True + + return False + + def load(self, content_type='yaml'): + ''' return yaml file ''' + contents = self.read() + + if not contents: + return None + + # check if it is yaml + try: + if content_type == 'yaml': + self.yaml_dict = yaml.load(contents) + elif content_type == 'json': + self.yaml_dict = json.loads(contents) + except yaml.YAMLError as _: + # Error loading yaml or json + return None + + return self.yaml_dict + + def get(self, key): + ''' get a specified key''' + try: + entry = Yedit.get_entry(self.yaml_dict, key) + except KeyError as _: + entry = None + + return entry + + def delete(self, key): + ''' remove key from a dict''' + try: + entry = Yedit.get_entry(self.yaml_dict, key) + except KeyError as _: + entry = None + if not entry: + return (False, self.yaml_dict) + + result = Yedit.remove_entry(self.yaml_dict, key) + if not result: + return (False, self.yaml_dict) + + return (True, self.yaml_dict) + + def put(self, key, value): + ''' put key, value into a dict ''' + try: + entry = Yedit.get_entry(self.yaml_dict, key) + except KeyError as _: + entry = None + + if entry == value: + return (False, self.yaml_dict) + + result = Yedit.add_entry(self.yaml_dict, key, value) + if not result: + return (False, self.yaml_dict) + + return (True, self.yaml_dict) + + def create(self, key, value): + ''' create a yaml file ''' + if not self.exists(): + self.yaml_dict = {key: value} + return (True, self.yaml_dict) + + return (False, self.yaml_dict) + +class OCObject(OpenShiftCLI): + ''' Class to wrap the oc command line tools ''' + + # pylint allows 5. we need 6 + # pylint: disable=too-many-arguments + def __init__(self, + kind, + namespace, + rname=None, + kubeconfig='/etc/origin/master/admin.kubeconfig', + verbose=False): + ''' Constructor for OpenshiftOC ''' + super(OCObject, self).__init__(namespace, kubeconfig) + self.kind = kind + self.namespace = namespace + self.name = rname + self.kubeconfig = kubeconfig + self.verbose = verbose + + def get(self): + '''return a deploymentconfig by name ''' + return self._get(self.kind, rname=self.name) + + def delete(self): + '''return all pods ''' + return self._delete(self.kind, self.name) + + def create(self, files=None, content=None): + '''Create a deploymentconfig ''' + if files: + return self._create(files[0]) + + return self._create(Utils.create_files_from_contents(content)) + + + # pylint: disable=too-many-function-args + def update(self, files=None, content=None, force=False): + '''run update dc + + This receives a list of file names and takes the first filename and calls replace. + ''' + if files: + return self._replace(files[0], force) + + return self.update_content(content, force) + + def update_content(self, content, force=False): + '''update the dc with the content''' + return self._replace_content(self.kind, self.name, content, force=force) + + def needs_update(self, files=None, content=None, content_type='yaml'): + ''' check to see if we need to update ''' + objects = self.get() + if objects['returncode'] != 0: + return objects + + # pylint: disable=no-member + data = None + if files: + data = Utils.get_resource_file(files[0], content_type) + + # if equal then no need. So not equal is True + return not Utils.check_def_equal(data, objects['results'][0], True) + else: + data = content + + for key, value in data.items(): + if key == 'metadata': + continue + if not objects['results'][0].has_key(key): + return True + if value != objects['results'][0][key]: + return True + + return False + + +# pylint: disable=too-many-branches +def main(): + ''' + ansible oc module for services + ''' + + module = AnsibleModule( + argument_spec=dict( + kubeconfig=dict(default='/etc/origin/master/admin.kubeconfig', type='str'), + state=dict(default='present', type='str', + choices=['present', 'absent', 'list']), + debug=dict(default=False, type='bool'), + namespace=dict(default='default', type='str'), + name=dict(default=None, type='str'), + files=dict(default=None, type='list'), + kind=dict(required=True, + type='str', + choices=['dc', 'deploymentconfig', + 'svc', 'service', + 'scc', 'securitycontextconstraints', + 'ns', 'namespace', 'project', 'projects', + 'is', 'imagestream', + 'istag', 'imagestreamtag', + 'bc', 'buildconfig', + 'routes', + 'node', + 'secret', + ]), + delete_after=dict(default=False, type='bool'), + content=dict(default=None, type='dict'), + force=dict(default=False, type='bool'), + ), + mutually_exclusive=[["content", "files"]], + + supports_check_mode=True, + ) + ocobj = OCObject(module.params['kind'], + module.params['namespace'], + module.params['name'], + kubeconfig=module.params['kubeconfig'], + verbose=module.params['debug']) + + state = module.params['state'] + + api_rval = ocobj.get() + + ##### + # Get + ##### + if state == 'list': + module.exit_json(changed=False, results=api_rval['results'], state="list") + + if not module.params['name']: + module.fail_json(msg='Please specify a name when state is absent|present.') + ######## + # Delete + ######## + if state == 'absent': + if not Utils.exists(api_rval['results'], module.params['name']): + module.exit_json(changed=False, state="absent") + + if module.check_mode: + module.exit_json(change=False, msg='Would have performed a delete.') + + api_rval = ocobj.delete() + module.exit_json(changed=True, results=api_rval, state="absent") + + if state == 'present': + ######## + # Create + ######## + if not Utils.exists(api_rval['results'], module.params['name']): + + if module.check_mode: + module.exit_json(change=False, msg='Would have performed a create.') + + # Create it here + api_rval = ocobj.create(module.params['files'], module.params['content']) + if api_rval['returncode'] != 0: + module.fail_json(msg=api_rval) + + # return the created object + api_rval = ocobj.get() + + if api_rval['returncode'] != 0: + module.fail_json(msg=api_rval) + + # Remove files + if module.params['files'] and module.params['delete_after']: + Utils.cleanup(module.params['files']) + + module.exit_json(changed=True, results=api_rval, state="present") + + ######## + # Update + ######## + # if a file path is passed, use it. + update = ocobj.needs_update(module.params['files'], module.params['content']) + if not isinstance(update, bool): + module.fail_json(msg=update) + + # No changes + if not update: + if module.params['files'] and module.params['delete_after']: + Utils.cleanup(module.params['files']) + + module.exit_json(changed=False, results=api_rval['results'][0], state="present") + + if module.check_mode: + module.exit_json(change=False, msg='Would have performed an update.') + + api_rval = ocobj.update(module.params['files'], + module.params['content'], + module.params['force']) + + + if api_rval['returncode'] != 0: + module.fail_json(msg=api_rval) + + # return the created object + api_rval = ocobj.get() + + if api_rval['returncode'] != 0: + module.fail_json(msg=api_rval) + + module.exit_json(changed=True, results=api_rval, state="present") + + module.exit_json(failed=True, + changed=False, + results='Unknown state passed. %s' % state, + state="unknown") + +# pylint: disable=redefined-builtin, unused-wildcard-import, wildcard-import, locally-disabled +# import module snippets. This are required +from ansible.module_utils.basic import * + +main() diff --git a/roles/lib_openshift_api/library/oc_secret.py b/roles/lib_openshift_api/library/oc_secret.py new file mode 100644 index 000000000..a03022e35 --- /dev/null +++ b/roles/lib_openshift_api/library/oc_secret.py @@ -0,0 +1,675 @@ +#!/usr/bin/env python +# ___ ___ _ _ ___ ___ _ _____ ___ ___ +# / __| __| \| | __| _ \ /_\_ _| __| \ +# | (_ | _|| .` | _|| / / _ \| | | _|| |) | +# \___|___|_|\_|___|_|_\/_/_\_\_|_|___|___/_ _____ +# | \ / _ \ | \| |/ _ \_ _| | __| \_ _|_ _| +# | |) | (_) | | .` | (_) || | | _|| |) | | | | +# |___/ \___/ |_|\_|\___/ |_| |___|___/___| |_| +''' + OpenShiftCLI class that wraps the oc commands in a subprocess +''' + +import atexit +import json +import os +import shutil +import subprocess +import re + +import yaml +# This is here because of a bug that causes yaml +# to incorrectly handle timezone info on timestamps +def timestamp_constructor(_, node): + '''return timestamps as strings''' + return str(node.value) +yaml.add_constructor(u'tag:yaml.org,2002:timestamp', timestamp_constructor) + +# pylint: disable=too-few-public-methods +class OpenShiftCLI(object): + ''' Class to wrap the oc command line tools ''' + def __init__(self, + namespace, + kubeconfig='/etc/origin/master/admin.kubeconfig', + verbose=False): + ''' Constructor for OpenshiftOC ''' + self.namespace = namespace + self.verbose = verbose + self.kubeconfig = kubeconfig + + # Pylint allows only 5 arguments to be passed. + # pylint: disable=too-many-arguments + def _replace_content(self, resource, rname, content, force=False): + ''' replace the current object with the content ''' + res = self._get(resource, rname) + if not res['results']: + return res + + fname = '/tmp/%s' % rname + yed = Yedit(fname, res['results'][0]) + changes = [] + for key, value in content.items(): + changes.append(yed.put(key, value)) + + if any([not change[0] for change in changes]): + return {'returncode': 0, 'updated': False} + + yed.write() + + atexit.register(Utils.cleanup, [fname]) + + return self._replace(fname, force) + + def _replace(self, fname, force=False): + '''return all pods ''' + cmd = ['-n', self.namespace, 'replace', '-f', fname] + if force: + cmd.append('--force') + return self.oc_cmd(cmd) + + def _create(self, fname): + '''return all pods ''' + return self.oc_cmd(['create', '-f', fname, '-n', self.namespace]) + + def _delete(self, resource, rname): + '''return all pods ''' + return self.oc_cmd(['delete', resource, rname, '-n', self.namespace]) + + def _get(self, resource, rname=None): + '''return a secret by name ''' + cmd = ['get', resource, '-o', 'json', '-n', self.namespace] + if rname: + cmd.append(rname) + + rval = self.oc_cmd(cmd, output=True) + + # Ensure results are retuned in an array + if rval.has_key('items'): + rval['results'] = rval['items'] + elif not isinstance(rval['results'], list): + rval['results'] = [rval['results']] + + return rval + + def oc_cmd(self, cmd, output=False): + '''Base command for oc ''' + #cmds = ['/usr/bin/oc', '--config', self.kubeconfig] + cmds = ['/usr/bin/oc'] + cmds.extend(cmd) + + rval = {} + results = '' + err = None + + if self.verbose: + print ' '.join(cmds) + + proc = subprocess.Popen(cmds, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env={'KUBECONFIG': self.kubeconfig}) + + proc.wait() + stdout = proc.stdout.read() + stderr = proc.stderr.read() + + rval = {"returncode": proc.returncode, + "results": results, + } + + if proc.returncode == 0: + if output: + try: + rval['results'] = json.loads(stdout) + except ValueError as err: + if "No JSON object could be decoded" in err.message: + err = err.message + + if self.verbose: + print stdout + print stderr + print + + if err: + rval.update({"err": err, + "stderr": stderr, + "stdout": stdout, + "cmd": cmds + }) + + else: + rval.update({"stderr": stderr, + "stdout": stdout, + "results": {}, + }) + + return rval + +class Utils(object): + ''' utilities for openshiftcli modules ''' + @staticmethod + def create_file(rname, data, ftype=None): + ''' create a file in tmp with name and contents''' + path = os.path.join('/tmp', rname) + with open(path, 'w') as fds: + if ftype == 'yaml': + fds.write(yaml.safe_dump(data, default_flow_style=False)) + + elif ftype == 'json': + fds.write(json.dumps(data)) + else: + fds.write(data) + + # Register cleanup when module is done + atexit.register(Utils.cleanup, [path]) + return path + + @staticmethod + def create_files_from_contents(data): + '''Turn an array of dict: filename, content into a files array''' + files = [] + + for sfile in data: + path = Utils.create_file(sfile['path'], sfile['content']) + files.append(path) + + return files + + @staticmethod + def cleanup(files): + '''Clean up on exit ''' + for sfile in files: + if os.path.exists(sfile): + if os.path.isdir(sfile): + shutil.rmtree(sfile) + elif os.path.isfile(sfile): + os.remove(sfile) + + + @staticmethod + def exists(results, _name): + ''' Check to see if the results include the name ''' + if not results: + return False + + + if Utils.find_result(results, _name): + return True + + return False + + @staticmethod + def find_result(results, _name): + ''' Find the specified result by name''' + rval = None + for result in results: + if result.has_key('metadata') and result['metadata']['name'] == _name: + rval = result + break + + return rval + + @staticmethod + def get_resource_file(sfile, sfile_type='yaml'): + ''' return the service file ''' + contents = None + with open(sfile) as sfd: + contents = sfd.read() + + if sfile_type == 'yaml': + contents = yaml.safe_load(contents) + elif sfile_type == 'json': + contents = json.loads(contents) + + return contents + + # Disabling too-many-branches. This is a yaml dictionary comparison function + # pylint: disable=too-many-branches,too-many-return-statements + @staticmethod + def check_def_equal(user_def, result_def, debug=False): + ''' Given a user defined definition, compare it with the results given back by our query. ''' + + # Currently these values are autogenerated and we do not need to check them + skip = ['metadata', 'status'] + + for key, value in result_def.items(): + if key in skip: + continue + + # Both are lists + if isinstance(value, list): + if not isinstance(user_def[key], list): + return False + + # lists should be identical + if value != user_def[key]: + return False + + # recurse on a dictionary + elif isinstance(value, dict): + if not isinstance(user_def[key], dict): + if debug: + print "dict returned false not instance of dict" + return False + + # before passing ensure keys match + api_values = set(value.keys()) - set(skip) + user_values = set(user_def[key].keys()) - set(skip) + if api_values != user_values: + if debug: + print api_values + print user_values + print "keys are not equal in dict" + return False + + result = Utils.check_def_equal(user_def[key], value, debug=debug) + if not result: + if debug: + print "dict returned false" + return False + + # Verify each key, value pair is the same + else: + if not user_def.has_key(key) or value != user_def[key]: + if debug: + print "value not equal; user_def does not have key" + print value + print user_def[key] + return False + + return True + +class YeditException(Exception): + ''' Exception class for Yedit ''' + pass + +class Yedit(object): + ''' Class to modify yaml files ''' + re_valid_key = r"(((\[-?\d+\])|([a-zA-Z-./]+)).?)+$" + re_key = r"(?:\[(-?\d+)\])|([a-zA-Z-./]+)" + + def __init__(self, filename=None, content=None, content_type='yaml'): + self.content = content + self.filename = filename + self.__yaml_dict = content + self.content_type = content_type + if self.filename and not self.content: + self.load(content_type=self.content_type) + + @property + def yaml_dict(self): + ''' getter method for yaml_dict ''' + return self.__yaml_dict + + @yaml_dict.setter + def yaml_dict(self, value): + ''' setter method for yaml_dict ''' + self.__yaml_dict = value + + @staticmethod + def remove_entry(data, key): + ''' remove data at location key ''' + if not (key and re.match(Yedit.re_valid_key, key) and isinstance(data, (list, dict))): + return None + + key_indexes = re.findall(Yedit.re_key, key) + for arr_ind, dict_key in key_indexes[:-1]: + if dict_key and isinstance(data, dict): + data = data.get(dict_key, None) + elif arr_ind and isinstance(data, list) and int(arr_ind) <= len(data) - 1: + data = data[int(arr_ind)] + else: + return None + + # process last index for remove + # expected list entry + if key_indexes[-1][0]: + if isinstance(data, list) and int(key_indexes[-1][0]) <= len(data) - 1: + del data[int(key_indexes[-1][0])] + return True + + # expected dict entry + elif key_indexes[-1][1]: + if isinstance(data, dict): + del data[key_indexes[-1][1]] + return True + + @staticmethod + def add_entry(data, key, item=None): + ''' Get an item from a dictionary with key notation a.b.c + d = {'a': {'b': 'c'}}} + key = a.b + return c + ''' + if not (key and re.match(Yedit.re_valid_key, key) and isinstance(data, (list, dict))): + return None + + curr_data = data + + key_indexes = re.findall(Yedit.re_key, key) + for arr_ind, dict_key in key_indexes[:-1]: + if dict_key: + if isinstance(data, dict) and data.has_key(dict_key): + data = data[dict_key] + continue + + data[dict_key] = {} + data = data[dict_key] + + elif arr_ind and isinstance(data, list) and int(arr_ind) <= len(data) - 1: + data = data[int(arr_ind)] + else: + return None + + # process last index for add + # expected list entry + if key_indexes[-1][0] and isinstance(data, list) and int(key_indexes[-1][0]) <= len(data) - 1: + data[int(key_indexes[-1][0])] = item + + # expected dict entry + elif key_indexes[-1][1] and isinstance(data, dict): + data[key_indexes[-1][1]] = item + + return curr_data + + @staticmethod + def get_entry(data, key): + ''' Get an item from a dictionary with key notation a.b.c + d = {'a': {'b': 'c'}}} + key = a.b + return c + ''' + if not (key and re.match(Yedit.re_valid_key, key) and isinstance(data, (list, dict))): + return None + + key_indexes = re.findall(Yedit.re_key, key) + for arr_ind, dict_key in key_indexes: + if dict_key and isinstance(data, dict): + data = data.get(dict_key, None) + elif arr_ind and isinstance(data, list) and int(arr_ind) <= len(data) - 1: + data = data[int(arr_ind)] + else: + return None + + return data + + def write(self): + ''' write to file ''' + if not self.filename: + raise YeditException('Please specify a filename.') + + with open(self.filename, 'w') as yfd: + yfd.write(yaml.safe_dump(self.yaml_dict, default_flow_style=False)) + + def read(self): + ''' write to file ''' + # check if it exists + if not self.exists(): + return None + + contents = None + with open(self.filename) as yfd: + contents = yfd.read() + + return contents + + def exists(self): + ''' return whether file exists ''' + if os.path.exists(self.filename): + return True + + return False + + def load(self, content_type='yaml'): + ''' return yaml file ''' + contents = self.read() + + if not contents: + return None + + # check if it is yaml + try: + if content_type == 'yaml': + self.yaml_dict = yaml.load(contents) + elif content_type == 'json': + self.yaml_dict = json.loads(contents) + except yaml.YAMLError as _: + # Error loading yaml or json + return None + + return self.yaml_dict + + def get(self, key): + ''' get a specified key''' + try: + entry = Yedit.get_entry(self.yaml_dict, key) + except KeyError as _: + entry = None + + return entry + + def delete(self, key): + ''' remove key from a dict''' + try: + entry = Yedit.get_entry(self.yaml_dict, key) + except KeyError as _: + entry = None + if not entry: + return (False, self.yaml_dict) + + result = Yedit.remove_entry(self.yaml_dict, key) + if not result: + return (False, self.yaml_dict) + + return (True, self.yaml_dict) + + def put(self, key, value): + ''' put key, value into a dict ''' + try: + entry = Yedit.get_entry(self.yaml_dict, key) + except KeyError as _: + entry = None + + if entry == value: + return (False, self.yaml_dict) + + result = Yedit.add_entry(self.yaml_dict, key, value) + if not result: + return (False, self.yaml_dict) + + return (True, self.yaml_dict) + + def create(self, key, value): + ''' create a yaml file ''' + if not self.exists(): + self.yaml_dict = {key: value} + return (True, self.yaml_dict) + + return (False, self.yaml_dict) + +class Secret(OpenShiftCLI): + ''' Class to wrap the oc command line tools + ''' + def __init__(self, + namespace, + secret_name=None, + kubeconfig='/etc/origin/master/admin.kubeconfig', + verbose=False): + ''' Constructor for OpenshiftOC ''' + super(Secret, self).__init__(namespace, kubeconfig) + self.namespace = namespace + self.name = secret_name + self.kubeconfig = kubeconfig + self.verbose = verbose + + def get(self): + '''return a secret by name ''' + return self._get('secrets', self.name) + + def delete(self): + '''delete a secret by name''' + return self._delete('secrets', self.name) + + def create(self, files=None, contents=None): + '''Create a secret ''' + if not files: + files = Utils.create_files_from_contents(contents) + + secrets = ["%s=%s" % (os.path.basename(sfile), sfile) for sfile in files] + cmd = ['-n%s' % self.namespace, 'secrets', 'new', self.name] + cmd.extend(secrets) + + return self.oc_cmd(cmd) + + def update(self, files, force=False): + '''run update secret + + This receives a list of file names and converts it into a secret. + The secret is then written to disk and passed into the `oc replace` command. + ''' + secret = self.prep_secret(files) + if secret['returncode'] != 0: + return secret + + sfile_path = '/tmp/%s' % self.name + with open(sfile_path, 'w') as sfd: + sfd.write(json.dumps(secret['results'])) + + atexit.register(Utils.cleanup, [sfile_path]) + + return self._replace(sfile_path, force=force) + + def prep_secret(self, files=None, contents=None): + ''' return what the secret would look like if created + This is accomplished by passing -ojson. This will most likely change in the future + ''' + if not files: + files = Utils.create_files_from_contents(contents) + + secrets = ["%s=%s" % (os.path.basename(sfile), sfile) for sfile in files] + cmd = ['-ojson', '-n%s' % self.namespace, 'secrets', 'new', self.name] + cmd.extend(secrets) + + return self.oc_cmd(cmd, output=True) + + + +# pylint: disable=too-many-branches +def main(): + ''' + ansible oc module for secrets + ''' + + module = AnsibleModule( + argument_spec=dict( + kubeconfig=dict(default='/etc/origin/master/admin.kubeconfig', type='str'), + state=dict(default='present', type='str', + choices=['present', 'absent', 'list']), + debug=dict(default=False, type='bool'), + namespace=dict(default='default', type='str'), + name=dict(default=None, type='str'), + files=dict(default=None, type='list'), + delete_after=dict(default=False, type='bool'), + contents=dict(default=None, type='list'), + force=dict(default=False, type='bool'), + ), + mutually_exclusive=[["contents", "files"]], + + supports_check_mode=True, + ) + occmd = Secret(module.params['namespace'], + module.params['name'], + kubeconfig=module.params['kubeconfig'], + verbose=module.params['debug']) + + state = module.params['state'] + + api_rval = occmd.get() + + ##### + # Get + ##### + if state == 'list': + module.exit_json(changed=False, results=api_rval['results'], state="list") + + if not module.params['name']: + module.fail_json(msg='Please specify a name when state is absent|present.') + ######## + # Delete + ######## + if state == 'absent': + if not Utils.exists(api_rval['results'], module.params['name']): + module.exit_json(changed=False, state="absent") + + if module.check_mode: + module.exit_json(change=False, msg='Would have performed a delete.') + + api_rval = occmd.delete() + module.exit_json(changed=True, results=api_rval, state="absent") + + + if state == 'present': + if module.params['files']: + files = module.params['files'] + elif module.params['contents']: + files = Utils.create_files_from_contents(module.params['contents']) + else: + module.fail_json(msg='Either specify files or contents.') + + ######## + # Create + ######## + if not Utils.exists(api_rval['results'], module.params['name']): + + if module.check_mode: + module.exit_json(change=False, msg='Would have performed a create.') + + api_rval = occmd.create(module.params['files'], module.params['contents']) + + # Remove files + if files and module.params['delete_after']: + Utils.cleanup(files) + + module.exit_json(changed=True, results=api_rval, state="present") + + ######## + # Update + ######## + secret = occmd.prep_secret(module.params['files'], module.params['contents']) + + if secret['returncode'] != 0: + module.fail_json(msg=secret) + + if Utils.check_def_equal(secret['results'], api_rval['results'][0]): + + # Remove files + if files and module.params['delete_after']: + Utils.cleanup(files) + + module.exit_json(changed=False, results=secret['results'], state="present") + + if module.check_mode: + module.exit_json(change=False, msg='Would have performed an update.') + + api_rval = occmd.update(files, force=module.params['force']) + + # Remove files + if secret and module.params['delete_after']: + Utils.cleanup(files) + + if api_rval['returncode'] != 0: + module.fail_json(msg=api_rval) + + + module.exit_json(changed=True, results=api_rval, state="present") + + module.exit_json(failed=True, + changed=False, + results='Unknown state passed. %s' % state, + state="unknown") + +# pylint: disable=redefined-builtin, unused-wildcard-import, wildcard-import, locally-disabled +# import module snippets. This are required +from ansible.module_utils.basic import * + +main() diff --git a/roles/lib_yaml_editor/build/ansible/yedit.py b/roles/lib_yaml_editor/build/ansible/yedit.py new file mode 100644 index 000000000..dab3d6347 --- /dev/null +++ b/roles/lib_yaml_editor/build/ansible/yedit.py @@ -0,0 +1,69 @@ +#pylint: skip-file + +def main(): + ''' + ansible oc module for secrets + ''' + + module = AnsibleModule( + argument_spec=dict( + state=dict(default='present', type='str', + choices=['present', 'absent', 'list']), + debug=dict(default=False, type='bool'), + src=dict(default=None, type='str'), + content=dict(default=None, type='dict'), + key=dict(default=None, type='str'), + value=dict(default=None, type='str'), + value_format=dict(default='yaml', choices=['yaml', 'json'], type='str'), + ), + #mutually_exclusive=[["src", "content"]], + + supports_check_mode=True, + ) + state = module.params['state'] + + yamlfile = Yedit(module.params['src'], module.params['content']) + + rval = yamlfile.load() + if not rval and state != 'present': + module.fail_json(msg='Error opening file [%s]. Verify that the' + \ + ' file exists, that it is has correct permissions, and is valid yaml.') + + if state == 'list': + module.exit_json(changed=False, results=rval, state="list") + + if state == 'absent': + rval = yamlfile.delete(module.params['key']) + module.exit_json(changed=rval[0], results=rval[1], state="absent") + + if state == 'present': + + if module.params['value_format'] == 'yaml': + value = yaml.load(module.params['value']) + elif module.params['value_format'] == 'json': + value = json.loads(module.params['value']) + + if rval: + rval = yamlfile.put(module.params['key'], value) + if rval[0]: + yamlfile.write() + module.exit_json(changed=rval[0], results=rval[1], state="present") + + if not module.params['content']: + rval = yamlfile.create(module.params['key'], value) + else: + rval = yamlfile.load() + yamlfile.write() + + module.exit_json(changed=rval[0], results=rval[1], state="present") + + module.exit_json(failed=True, + changed=False, + results='Unknown state passed. %s' % state, + state="unknown") + +# pylint: disable=redefined-builtin, unused-wildcard-import, wildcard-import, locally-disabled +# import module snippets. This are required +from ansible.module_utils.basic import * + +main() diff --git a/roles/lib_yaml_editor/build/generate.py b/roles/lib_yaml_editor/build/generate.py new file mode 100755 index 000000000..312e4d0ee --- /dev/null +++ b/roles/lib_yaml_editor/build/generate.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +''' + Generate the openshift-ansible/roles/lib_openshift_cli/library/ modules. +''' + +import os + +# pylint: disable=anomalous-backslash-in-string +GEN_STR = "#!/usr/bin/env python\n" + \ + "# ___ ___ _ _ ___ ___ _ _____ ___ ___\n" + \ + "# / __| __| \| | __| _ \ /_\_ _| __| \\\n" + \ + "# | (_ | _|| .` | _|| / / _ \| | | _|| |) |\n" + \ + "# \___|___|_|\_|___|_|_\/_/_\_\_|_|___|___/_ _____\n" + \ + "# | \ / _ \ | \| |/ _ \_ _| | __| \_ _|_ _|\n" + \ + "# | |) | (_) | | .` | (_) || | | _|| |) | | | |\n" + \ + "# |___/ \___/ |_|\_|\___/ |_| |___|___/___| |_|\n" + +OPENSHIFT_ANSIBLE_PATH = os.path.dirname(os.path.realpath(__file__)) + +FILES = {'yedit.py': ['src/base.py', 'src/yedit.py', 'ansible/yedit.py'], + } + +def main(): + ''' combine the necessary files to create the ansible module ''' + library = os.path.join(OPENSHIFT_ANSIBLE_PATH, '..', 'library/') + for fname, parts in FILES.items(): + with open(os.path.join(library, fname), 'w') as afd: + afd.seek(0) + afd.write(GEN_STR) + for fpart in parts: + with open(os.path.join(OPENSHIFT_ANSIBLE_PATH, fpart)) as pfd: + # first line is pylint disable so skip it + for idx, line in enumerate(pfd): + if idx == 0 and 'skip-file' in line: + continue + + afd.write(line) + + +if __name__ == '__main__': + main() + + diff --git a/roles/lib_yaml_editor/build/src/base.py b/roles/lib_yaml_editor/build/src/base.py new file mode 100644 index 000000000..9e43d45dc --- /dev/null +++ b/roles/lib_yaml_editor/build/src/base.py @@ -0,0 +1,17 @@ +# pylint: skip-file + +''' +module for managing yaml files +''' + +import os +import re + +import yaml +# This is here because of a bug that causes yaml +# to incorrectly handle timezone info on timestamps +def timestamp_constructor(_, node): + ''' return timestamps as strings''' + return str(node.value) +yaml.add_constructor(u'tag:yaml.org,2002:timestamp', timestamp_constructor) + diff --git a/roles/lib_yaml_editor/build/src/yedit.py b/roles/lib_yaml_editor/build/src/yedit.py new file mode 100644 index 000000000..642733914 --- /dev/null +++ b/roles/lib_yaml_editor/build/src/yedit.py @@ -0,0 +1,209 @@ +# pylint: skip-file + +class YeditException(Exception): + ''' Exception class for Yedit ''' + pass + +class Yedit(object): + ''' Class to modify yaml files ''' + re_valid_key = r"(((\[-?\d+\])|([a-zA-Z-./]+)).?)+$" + re_key = r"(?:\[(-?\d+)\])|([a-zA-Z-./]+)" + + def __init__(self, filename=None, content=None, content_type='yaml'): + self.content = content + self.filename = filename + self.__yaml_dict = content + self.content_type = content_type + if self.filename and not self.content: + self.load(content_type=self.content_type) + + @property + def yaml_dict(self): + ''' getter method for yaml_dict ''' + return self.__yaml_dict + + @yaml_dict.setter + def yaml_dict(self, value): + ''' setter method for yaml_dict ''' + self.__yaml_dict = value + + @staticmethod + def remove_entry(data, key): + ''' remove data at location key ''' + if not (key and re.match(Yedit.re_valid_key, key) and isinstance(data, (list, dict))): + return None + + key_indexes = re.findall(Yedit.re_key, key) + for arr_ind, dict_key in key_indexes[:-1]: + if dict_key and isinstance(data, dict): + data = data.get(dict_key, None) + elif arr_ind and isinstance(data, list) and int(arr_ind) <= len(data) - 1: + data = data[int(arr_ind)] + else: + return None + + # process last index for remove + # expected list entry + if key_indexes[-1][0]: + if isinstance(data, list) and int(key_indexes[-1][0]) <= len(data) - 1: + del data[int(key_indexes[-1][0])] + return True + + # expected dict entry + elif key_indexes[-1][1]: + if isinstance(data, dict): + del data[key_indexes[-1][1]] + return True + + @staticmethod + def add_entry(data, key, item=None): + ''' Get an item from a dictionary with key notation a.b.c + d = {'a': {'b': 'c'}}} + key = a.b + return c + ''' + if not (key and re.match(Yedit.re_valid_key, key) and isinstance(data, (list, dict))): + return None + + curr_data = data + + key_indexes = re.findall(Yedit.re_key, key) + for arr_ind, dict_key in key_indexes[:-1]: + if dict_key: + if isinstance(data, dict) and data.has_key(dict_key): + data = data[dict_key] + continue + + data[dict_key] = {} + data = data[dict_key] + + elif arr_ind and isinstance(data, list) and int(arr_ind) <= len(data) - 1: + data = data[int(arr_ind)] + else: + return None + + # process last index for add + # expected list entry + if key_indexes[-1][0] and isinstance(data, list) and int(key_indexes[-1][0]) <= len(data) - 1: + data[int(key_indexes[-1][0])] = item + + # expected dict entry + elif key_indexes[-1][1] and isinstance(data, dict): + data[key_indexes[-1][1]] = item + + return curr_data + + @staticmethod + def get_entry(data, key): + ''' Get an item from a dictionary with key notation a.b.c + d = {'a': {'b': 'c'}}} + key = a.b + return c + ''' + if not (key and re.match(Yedit.re_valid_key, key) and isinstance(data, (list, dict))): + return None + + key_indexes = re.findall(Yedit.re_key, key) + for arr_ind, dict_key in key_indexes: + if dict_key and isinstance(data, dict): + data = data.get(dict_key, None) + elif arr_ind and isinstance(data, list) and int(arr_ind) <= len(data) - 1: + data = data[int(arr_ind)] + else: + return None + + return data + + def write(self): + ''' write to file ''' + if not self.filename: + raise YeditException('Please specify a filename.') + + with open(self.filename, 'w') as yfd: + yfd.write(yaml.safe_dump(self.yaml_dict, default_flow_style=False)) + + def read(self): + ''' write to file ''' + # check if it exists + if not self.exists(): + return None + + contents = None + with open(self.filename) as yfd: + contents = yfd.read() + + return contents + + def exists(self): + ''' return whether file exists ''' + if os.path.exists(self.filename): + return True + + return False + + def load(self, content_type='yaml'): + ''' return yaml file ''' + contents = self.read() + + if not contents: + return None + + # check if it is yaml + try: + if content_type == 'yaml': + self.yaml_dict = yaml.load(contents) + elif content_type == 'json': + self.yaml_dict = json.loads(contents) + except yaml.YAMLError as _: + # Error loading yaml or json + return None + + return self.yaml_dict + + def get(self, key): + ''' get a specified key''' + try: + entry = Yedit.get_entry(self.yaml_dict, key) + except KeyError as _: + entry = None + + return entry + + def delete(self, key): + ''' remove key from a dict''' + try: + entry = Yedit.get_entry(self.yaml_dict, key) + except KeyError as _: + entry = None + if not entry: + return (False, self.yaml_dict) + + result = Yedit.remove_entry(self.yaml_dict, key) + if not result: + return (False, self.yaml_dict) + + return (True, self.yaml_dict) + + def put(self, key, value): + ''' put key, value into a dict ''' + try: + entry = Yedit.get_entry(self.yaml_dict, key) + except KeyError as _: + entry = None + + if entry == value: + return (False, self.yaml_dict) + + result = Yedit.add_entry(self.yaml_dict, key, value) + if not result: + return (False, self.yaml_dict) + + return (True, self.yaml_dict) + + def create(self, key, value): + ''' create a yaml file ''' + if not self.exists(): + self.yaml_dict = {key: value} + return (True, self.yaml_dict) + + return (False, self.yaml_dict) diff --git a/roles/lib_yaml_editor/build/test/foo.yml b/roles/lib_yaml_editor/build/test/foo.yml new file mode 100644 index 000000000..20e9ff3fe --- /dev/null +++ b/roles/lib_yaml_editor/build/test/foo.yml @@ -0,0 +1 @@ +foo: bar diff --git a/roles/lib_yaml_editor/build/test/test.yaml b/roles/lib_yaml_editor/build/test/test.yaml new file mode 100755 index 000000000..ac9c37565 --- /dev/null +++ b/roles/lib_yaml_editor/build/test/test.yaml @@ -0,0 +1,15 @@ +#!/usr/bin/ansible-playbook +--- +- hosts: localhost + gather_facts: no + tasks: + - yedit: + src: /home/kwoodson/git/openshift-ansible/roles/lib_yaml_editor/build/test/foo.yml + key: foo + value: barplus + state: present + register: output + + - debug: + msg: "{{ output }}" + diff --git a/roles/lib_yaml_editor/library/yedit.py b/roles/lib_yaml_editor/library/yedit.py new file mode 100644 index 000000000..b7ae45b31 --- /dev/null +++ b/roles/lib_yaml_editor/library/yedit.py @@ -0,0 +1,300 @@ +#!/usr/bin/env python +# ___ ___ _ _ ___ ___ _ _____ ___ ___ +# / __| __| \| | __| _ \ /_\_ _| __| \ +# | (_ | _|| .` | _|| / / _ \| | | _|| |) | +# \___|___|_|\_|___|_|_\/_/_\_\_|_|___|___/_ _____ +# | \ / _ \ | \| |/ _ \_ _| | __| \_ _|_ _| +# | |) | (_) | | .` | (_) || | | _|| |) | | | | +# |___/ \___/ |_|\_|\___/ |_| |___|___/___| |_| + +''' +module for managing yaml files +''' + +import os +import re + +import yaml +# This is here because of a bug that causes yaml +# to incorrectly handle timezone info on timestamps +def timestamp_constructor(_, node): + ''' return timestamps as strings''' + return str(node.value) +yaml.add_constructor(u'tag:yaml.org,2002:timestamp', timestamp_constructor) + + +class YeditException(Exception): + ''' Exception class for Yedit ''' + pass + +class Yedit(object): + ''' Class to modify yaml files ''' + re_valid_key = r"(((\[-?\d+\])|([a-zA-Z-./]+)).?)+$" + re_key = r"(?:\[(-?\d+)\])|([a-zA-Z-./]+)" + + def __init__(self, filename=None, content=None, content_type='yaml'): + self.content = content + self.filename = filename + self.__yaml_dict = content + self.content_type = content_type + if self.filename and not self.content: + self.load(content_type=self.content_type) + + @property + def yaml_dict(self): + ''' getter method for yaml_dict ''' + return self.__yaml_dict + + @yaml_dict.setter + def yaml_dict(self, value): + ''' setter method for yaml_dict ''' + self.__yaml_dict = value + + @staticmethod + def remove_entry(data, key): + ''' remove data at location key ''' + if not (key and re.match(Yedit.re_valid_key, key) and isinstance(data, (list, dict))): + return None + + key_indexes = re.findall(Yedit.re_key, key) + for arr_ind, dict_key in key_indexes[:-1]: + if dict_key and isinstance(data, dict): + data = data.get(dict_key, None) + elif arr_ind and isinstance(data, list) and int(arr_ind) <= len(data) - 1: + data = data[int(arr_ind)] + else: + return None + + # process last index for remove + # expected list entry + if key_indexes[-1][0]: + if isinstance(data, list) and int(key_indexes[-1][0]) <= len(data) - 1: + del data[int(key_indexes[-1][0])] + return True + + # expected dict entry + elif key_indexes[-1][1]: + if isinstance(data, dict): + del data[key_indexes[-1][1]] + return True + + @staticmethod + def add_entry(data, key, item=None): + ''' Get an item from a dictionary with key notation a.b.c + d = {'a': {'b': 'c'}}} + key = a.b + return c + ''' + if not (key and re.match(Yedit.re_valid_key, key) and isinstance(data, (list, dict))): + return None + + curr_data = data + + key_indexes = re.findall(Yedit.re_key, key) + for arr_ind, dict_key in key_indexes[:-1]: + if dict_key: + if isinstance(data, dict) and data.has_key(dict_key): + data = data[dict_key] + continue + + data[dict_key] = {} + data = data[dict_key] + + elif arr_ind and isinstance(data, list) and int(arr_ind) <= len(data) - 1: + data = data[int(arr_ind)] + else: + return None + + # process last index for add + # expected list entry + if key_indexes[-1][0] and isinstance(data, list) and int(key_indexes[-1][0]) <= len(data) - 1: + data[int(key_indexes[-1][0])] = item + + # expected dict entry + elif key_indexes[-1][1] and isinstance(data, dict): + data[key_indexes[-1][1]] = item + + return curr_data + + @staticmethod + def get_entry(data, key): + ''' Get an item from a dictionary with key notation a.b.c + d = {'a': {'b': 'c'}}} + key = a.b + return c + ''' + if not (key and re.match(Yedit.re_valid_key, key) and isinstance(data, (list, dict))): + return None + + key_indexes = re.findall(Yedit.re_key, key) + for arr_ind, dict_key in key_indexes: + if dict_key and isinstance(data, dict): + data = data.get(dict_key, None) + elif arr_ind and isinstance(data, list) and int(arr_ind) <= len(data) - 1: + data = data[int(arr_ind)] + else: + return None + + return data + + def write(self): + ''' write to file ''' + if not self.filename: + raise YeditException('Please specify a filename.') + + with open(self.filename, 'w') as yfd: + yfd.write(yaml.safe_dump(self.yaml_dict, default_flow_style=False)) + + def read(self): + ''' write to file ''' + # check if it exists + if not self.exists(): + return None + + contents = None + with open(self.filename) as yfd: + contents = yfd.read() + + return contents + + def exists(self): + ''' return whether file exists ''' + if os.path.exists(self.filename): + return True + + return False + + def load(self, content_type='yaml'): + ''' return yaml file ''' + contents = self.read() + + if not contents: + return None + + # check if it is yaml + try: + if content_type == 'yaml': + self.yaml_dict = yaml.load(contents) + elif content_type == 'json': + self.yaml_dict = json.loads(contents) + except yaml.YAMLError as _: + # Error loading yaml or json + return None + + return self.yaml_dict + + def get(self, key): + ''' get a specified key''' + try: + entry = Yedit.get_entry(self.yaml_dict, key) + except KeyError as _: + entry = None + + return entry + + def delete(self, key): + ''' remove key from a dict''' + try: + entry = Yedit.get_entry(self.yaml_dict, key) + except KeyError as _: + entry = None + if not entry: + return (False, self.yaml_dict) + + result = Yedit.remove_entry(self.yaml_dict, key) + if not result: + return (False, self.yaml_dict) + + return (True, self.yaml_dict) + + def put(self, key, value): + ''' put key, value into a dict ''' + try: + entry = Yedit.get_entry(self.yaml_dict, key) + except KeyError as _: + entry = None + + if entry == value: + return (False, self.yaml_dict) + + result = Yedit.add_entry(self.yaml_dict, key, value) + if not result: + return (False, self.yaml_dict) + + return (True, self.yaml_dict) + + def create(self, key, value): + ''' create a yaml file ''' + if not self.exists(): + self.yaml_dict = {key: value} + return (True, self.yaml_dict) + + return (False, self.yaml_dict) + +def main(): + ''' + ansible oc module for secrets + ''' + + module = AnsibleModule( + argument_spec=dict( + state=dict(default='present', type='str', + choices=['present', 'absent', 'list']), + debug=dict(default=False, type='bool'), + src=dict(default=None, type='str'), + content=dict(default=None, type='dict'), + key=dict(default=None, type='str'), + value=dict(default=None, type='str'), + value_format=dict(default='yaml', choices=['yaml', 'json'], type='str'), + ), + #mutually_exclusive=[["src", "content"]], + + supports_check_mode=True, + ) + state = module.params['state'] + + yamlfile = Yedit(module.params['src'], module.params['content']) + + rval = yamlfile.load() + if not rval and state != 'present': + module.fail_json(msg='Error opening file [%s]. Verify that the' + \ + ' file exists, that it is has correct permissions, and is valid yaml.') + + if state == 'list': + module.exit_json(changed=False, results=rval, state="list") + + if state == 'absent': + rval = yamlfile.delete(module.params['key']) + module.exit_json(changed=rval[0], results=rval[1], state="absent") + + if state == 'present': + + if module.params['value_format'] == 'yaml': + value = yaml.load(module.params['value']) + elif module.params['value_format'] == 'json': + value = json.loads(module.params['value']) + + if rval: + rval = yamlfile.put(module.params['key'], value) + if rval[0]: + yamlfile.write() + module.exit_json(changed=rval[0], results=rval[1], state="present") + + if not module.params['content']: + rval = yamlfile.create(module.params['key'], value) + else: + rval = yamlfile.load() + yamlfile.write() + + module.exit_json(changed=rval[0], results=rval[1], state="present") + + module.exit_json(failed=True, + changed=False, + results='Unknown state passed. %s' % state, + state="unknown") + +# pylint: disable=redefined-builtin, unused-wildcard-import, wildcard-import, locally-disabled +# import module snippets. This are required +from ansible.module_utils.basic import * + +main() diff --git a/roles/nuage_master/templates/nuage-openshift-monitor.j2 b/roles/nuage_master/templates/nuage-openshift-monitor.j2 index e50e225e1..7228e646b 100644 --- a/roles/nuage_master/templates/nuage-openshift-monitor.j2 +++ b/roles/nuage_master/templates/nuage-openshift-monitor.j2 @@ -18,6 +18,10 @@ domainName: {{ domain }} # Location where logs should be saved log_dir: {{ nuage_mon_rest_server_logdir }} # Monitor rest server paramters +# Logging level for the nuage openshift monitor +# allowed options are: 0 => INFO, 1 => WARNING, 2 => ERROR, 3 => FATAL +logLevel: {{ nuage_mon_log_level }} +# Parameters related to the nuage monitor REST server nuageMonServer: URL: {{ nuage_mon_rest_server_url }} certificateDirectory: {{ cert_output_dir }} diff --git a/roles/nuage_master/vars/main.yaml b/roles/nuage_master/vars/main.yaml index 4b57273e4..ec4562c77 100644 --- a/roles/nuage_master/vars/main.yaml +++ b/roles/nuage_master/vars/main.yaml @@ -8,6 +8,7 @@ master_config_yaml: "{{ openshift_master_config_dir }}/master-config.yaml" nuage_mon_rest_server_port: "{{ nuage_openshift_monitor_rest_server_port | default('9443') }}" nuage_mon_rest_server_url: "0.0.0.0:{{ nuage_mon_rest_server_port }}" nuage_mon_rest_server_logdir: "{{ nuage_openshift_monitor_log_dir | default('/var/log/nuage-openshift-monitor') }}" +nuage_mon_log_level: "{{ nuage_openshift_monitor_log_level | default('3') }}" nuage_mon_rest_server_crt_dir: "{{ nuage_ca_master_crt_dir }}/{{ ansible_nodename }}" nuage_ca_master_rest_server_key: "{{ nuage_mon_rest_server_crt_dir }}/nuageMonServer.key" diff --git a/roles/nuage_node/templates/vsp-openshift.j2 b/roles/nuage_node/templates/vsp-openshift.j2 index 6c10b9c24..d3c0a122a 100644 --- a/roles/nuage_node/templates/vsp-openshift.j2 +++ b/roles/nuage_node/templates/vsp-openshift.j2 @@ -22,3 +22,6 @@ nuageMonClientKey: {{ rest_client_key }} nuageMonServerCA: {{ rest_server_ca_cert }} # Nuage vport mtu size interfaceMTU: {{ vport_mtu }} +# Logging level for the plugin +# allowed options are: "dbg", "info", "warn", "err", "emer", "off" +logLevel: {{ plugin_log_level }} diff --git a/roles/nuage_node/vars/main.yaml b/roles/nuage_node/vars/main.yaml index 5acc65ef4..a4d7052a7 100644 --- a/roles/nuage_node/vars/main.yaml +++ b/roles/nuage_node/vars/main.yaml @@ -13,6 +13,7 @@ rest_client_cert: "{{ vsp_openshift_dir }}/nuageMonClient.crt" rest_client_key: "{{ vsp_openshift_dir }}/nuageMonClient.key" rest_server_ca_cert: "{{ vsp_openshift_dir }}/nuageMonCA.crt" vport_mtu: "{{ nuage_interface_mtu | default('1460') }}" +plugin_log_level: "{{ nuage_plugin_log_level | default('err') }}" nuage_plugin_rest_client_crt_dir: "{{ nuage_ca_master_crt_dir }}/{{ ansible_nodename }}" nuage_ca_master_plugin_key: "{{ nuage_plugin_rest_client_crt_dir }}/nuageMonClient.key" diff --git a/roles/openshift_cli/meta/main.yml b/roles/openshift_cli/meta/main.yml index 1e8f8b719..223cb768d 100644 --- a/roles/openshift_cli/meta/main.yml +++ b/roles/openshift_cli/meta/main.yml @@ -12,5 +12,6 @@ galaxy_info: categories: - cloud dependencies: -- { role: openshift_common } -- { role: docker } +- role: openshift_docker +- role: openshift_common +- role: openshift_cli_facts diff --git a/roles/openshift_cli/tasks/main.yml b/roles/openshift_cli/tasks/main.yml index 3b224416e..bfa60e5b0 100644 --- a/roles/openshift_cli/tasks/main.yml +++ b/roles/openshift_cli/tasks/main.yml @@ -1,10 +1,4 @@ --- -- openshift_facts: - role: common - local_facts: - deployment_type: "{{ openshift_deployment_type }}" - cli_image: "{{ osm_image | default(None) }}" - - name: Install clients action: "{{ ansible_pkg_mgr }} name={{ openshift.common.service_type }}-clients state=present" when: not openshift.common.is_containerized | bool @@ -13,16 +7,16 @@ command: > docker pull {{ openshift.common.cli_image }}:{{ openshift_version }} when: openshift.common.is_containerized | bool - + - name: Create /usr/local/bin/openshift cli wrapper template: src: openshift.j2 dest: /usr/local/bin/openshift mode: 0755 when: openshift.common.is_containerized | bool - + - name: Create client symlinks - file: + file: path: "{{ item }}" state: link src: /usr/local/bin/openshift diff --git a/roles/openshift_cli_facts/meta/main.yml b/roles/openshift_cli_facts/meta/main.yml new file mode 100644 index 000000000..59acde215 --- /dev/null +++ b/roles/openshift_cli_facts/meta/main.yml @@ -0,0 +1,15 @@ +--- +galaxy_info: + author: Jason DeTiberus + description: OpenShift CLI Facts + company: Red Hat, Inc. + license: Apache License, Version 2.0 + min_ansible_version: 1.9 + platforms: + - name: EL + versions: + - 7 + categories: + - cloud +dependencies: +- role: openshift_facts diff --git a/roles/openshift_cli_facts/tasks/main.yml b/roles/openshift_cli_facts/tasks/main.yml new file mode 100644 index 000000000..dd1ed8965 --- /dev/null +++ b/roles/openshift_cli_facts/tasks/main.yml @@ -0,0 +1,6 @@ +--- +# TODO: move this to a new 'cli' role +- openshift_facts: + role: common + local_facts: + cli_image: "{{ osm_image | default(None) }}" diff --git a/roles/openshift_cloud_provider/meta/main.yml b/roles/openshift_cloud_provider/meta/main.yml new file mode 100644 index 000000000..8ab95bf5a --- /dev/null +++ b/roles/openshift_cloud_provider/meta/main.yml @@ -0,0 +1,15 @@ +--- +galaxy_info: + author: Sylvain Baubeau, Andrew Butcher + description: OpenShift Cloud Provider + company: Red Hat, Inc. + license: Apache License, Version 2.0 + min_ansible_version: 1.9 + platforms: + - name: EL + versions: + - 7 + categories: + - cloud +dependencies: +- role: openshift_facts diff --git a/roles/openshift_cloud_provider/tasks/aws.yml b/roles/openshift_cloud_provider/tasks/aws.yml new file mode 100644 index 000000000..bf2abcbf5 --- /dev/null +++ b/roles/openshift_cloud_provider/tasks/aws.yml @@ -0,0 +1,6 @@ +- name: Create cloud config + ini_file: + dest: "{{ openshift.common.config_base }}/cloudprovider/aws.conf" + section: Global + option: Zone + value: "{{ openshift.provider.zone }}" diff --git a/roles/openshift_cloud_provider/tasks/main.yml b/roles/openshift_cloud_provider/tasks/main.yml new file mode 100644 index 000000000..471fd686b --- /dev/null +++ b/roles/openshift_cloud_provider/tasks/main.yml @@ -0,0 +1,24 @@ +--- +- name: Set cloud provider facts + openshift_facts: + role: cloudprovider + openshift_env: "{{ item | oo_openshift_env }}" + openshift_env_structures: + - 'openshift.cloudprovider.aws.*' + - 'openshift.cloudprovider.openstack.*' + no_log: true + with_items: + - "{{ hostvars[inventory_hostname] }}" + - "{{ hostvars }}" + +- name: Create cloudprovider config dir + file: + path: "{{ openshift.common.config_base }}/cloudprovider" + state: directory + when: has_cloudprovider | bool + +- include: openstack.yml + when: cloudprovider_is_openstack | bool + +- include: aws.yml + when: cloudprovider_is_aws | bool diff --git a/roles/openshift_cloud_provider/tasks/openstack.yml b/roles/openshift_cloud_provider/tasks/openstack.yml new file mode 100644 index 000000000..c501121e5 --- /dev/null +++ b/roles/openshift_cloud_provider/tasks/openstack.yml @@ -0,0 +1,10 @@ +--- +- fail: + msg: "The Openstack integration requires OpenShift Enterprise 3.2 or Origin 1.2." + when: not openshift.common.version_gte_3_2_or_1_2 | bool + +- name: Create cloud config + template: + dest: "{{ openshift.common.config_base }}/cloudprovider/openstack.conf" + src: openstack.conf.j2 + when: "'auth_url' in openshift.cloudprovider.openstack and 'username' in openshift.cloudprovider.openstack and 'password' in openshift.cloudprovider.openstack and ('tenant_id' in openshift.cloudprovider.openstack or 'tenant_name' in openshift.cloudprovider.openstack)" diff --git a/roles/openshift_cloud_provider/templates/openstack.conf.j2 b/roles/openshift_cloud_provider/templates/openstack.conf.j2 new file mode 100644 index 000000000..1b70edc16 --- /dev/null +++ b/roles/openshift_cloud_provider/templates/openstack.conf.j2 @@ -0,0 +1,17 @@ +[Global] +auth-url = {{ openshift.cloudprovider.openstack.auth_url }} +username = {{ openshift.cloudprovider.openstack.username }} +password = {{ openshift.cloudprovider.openstack.password }} +{% if 'tenant_id' in openshift.cloudprovider.openstack %} +tenant-id = {{ openshift.cloudprovider.openstack.tenant_id }} +{% else %} +tenant-name = {{ openshift.cloudprovider.openstack.tenant_name }} +{% endif %} +{% if 'region' in openshift.cloudprovider.openstack %} +region = {{ openshift.cloudprovider.openstack.region }} +{% endif %} +{% if 'lb_subnet_id' in openshift.cloudprovider.openstack %} ++ ++[LoadBalancer] ++subnet-id = {{ openshift.cloudprovider.openstack.lb_subnet_id }} ++{% endif %} diff --git a/roles/openshift_cloud_provider/vars/main.yml b/roles/openshift_cloud_provider/vars/main.yml new file mode 100644 index 000000000..c608e9b54 --- /dev/null +++ b/roles/openshift_cloud_provider/vars/main.yml @@ -0,0 +1,4 @@ +--- +has_cloudprovider: "{{ 'cloudprovider' in openshift and 'kind' in openshift.cloudprovider and openshift.cloudprovider.kind != None }}" +cloudprovider_is_aws: "{{ has_cloudprovider | bool and openshift.cloudprovider.kind == 'aws' }}" +cloudprovider_is_openstack: "{{ has_cloudprovider | bool and openshift.cloudprovider.kind == 'openstack' }}" diff --git a/roles/openshift_common/meta/main.yml b/roles/openshift_common/meta/main.yml index 81363ec68..02150406d 100644 --- a/roles/openshift_common/meta/main.yml +++ b/roles/openshift_common/meta/main.yml @@ -12,6 +12,6 @@ galaxy_info: categories: - cloud dependencies: -- { role: os_firewall } -- { role: openshift_facts } -- { role: openshift_repos } +- role: os_firewall +- role: openshift_facts +- role: openshift_repos diff --git a/roles/openshift_common/tasks/main.yml b/roles/openshift_common/tasks/main.yml index a2fcff507..b6074ff64 100644 --- a/roles/openshift_common/tasks/main.yml +++ b/roles/openshift_common/tasks/main.yml @@ -19,23 +19,21 @@ openshift_facts: role: common local_facts: - cluster_id: "{{ openshift_cluster_id | default('default') }}" debug_level: "{{ openshift_debug_level | default(2) }}" - hostname: "{{ openshift_hostname | default(None) }}" install_examples: "{{ openshift_install_examples | default(True) }}" - ip: "{{ openshift_ip | default(None) }}" - public_hostname: "{{ openshift_public_hostname | default(None) }}" - public_ip: "{{ openshift_public_ip | default(None) }}" use_openshift_sdn: "{{ openshift_use_openshift_sdn | default(None) }}" sdn_network_plugin_name: "{{ os_sdn_network_plugin_name | default(None) }}" - deployment_type: "{{ openshift_deployment_type }}" use_flannel: "{{ openshift_use_flannel | default(None) }}" use_nuage: "{{ openshift_use_nuage | default(None) }}" use_manageiq: "{{ openshift_use_manageiq | default(None) }}" data_dir: "{{ openshift_data_dir | default(None) }}" +# Using oo_image_tag_to_rpm_version here is a workaround for how +# openshift_version is set. That value is computed based on either RPM +# versions or image tags. openshift_common's usage requires that it be a RPM +# version and openshift_cli expects it to be an image tag. - name: Install the base package for versioning - action: "{{ ansible_pkg_mgr }} name={{ openshift.common.service_type }}{{ openshift_version | default('') }} state=present" + action: "{{ ansible_pkg_mgr }} name={{ openshift.common.service_type }}{{ openshift_version | default('') | oo_image_tag_to_rpm_version }} state=present" when: not openshift.common.is_containerized | bool # This invocation also updates the version facts which are necessary diff --git a/roles/openshift_docker/handlers/main.yml b/roles/openshift_docker/handlers/main.yml deleted file mode 100644 index 92a6c325f..000000000 --- a/roles/openshift_docker/handlers/main.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- - -- name: restart openshift_docker - service: - name: docker - state: restarted diff --git a/roles/openshift_docker/meta/main.yml b/roles/openshift_docker/meta/main.yml index 1e8f8b719..d98f953ea 100644 --- a/roles/openshift_docker/meta/main.yml +++ b/roles/openshift_docker/meta/main.yml @@ -12,5 +12,6 @@ galaxy_info: categories: - cloud dependencies: -- { role: openshift_common } -- { role: docker } +- role: openshift_repos +- role: openshift_docker_facts +- role: docker diff --git a/roles/openshift_docker/tasks/main.yml b/roles/openshift_docker/tasks/main.yml deleted file mode 100644 index a57cf815e..000000000 --- a/roles/openshift_docker/tasks/main.yml +++ /dev/null @@ -1,55 +0,0 @@ ---- -- name: Set docker facts - openshift_facts: - role: "{{ item.role }}" - local_facts: "{{ item.local_facts }}" - with_items: - - role: common - local_facts: - deployment_type: "{{ openshift_deployment_type }}" - docker_additional_registries: "{{ docker_additional_registries }}" - docker_insecure_registries: "{{ docker_insecure_registries }}" - docker_blocked_registries: "{{ docker_blocked_registries }}" - docker_options: "{{ openshift_docker_options | default('',True) }}" - - role: node - local_facts: - portal_net: "{{ openshift_master_portal_net | default(None) }}" - docker_log_driver: "{{ lookup( 'oo_option' , 'docker_log_driver' ) | default('',True) }}" - docker_log_options: "{{ lookup( 'oo_option' , 'docker_log_options' ) | default('',True) }}" - -- stat: path=/etc/sysconfig/docker - register: docker_check - -- name: Set registry params - lineinfile: - dest: /etc/sysconfig/docker - regexp: '^{{ item.reg_conf_var }}=.*$' - line: "{{ item.reg_conf_var }}='{{ item.reg_fact_val | oo_prepend_strings_in_list(item.reg_flag ~ ' ') | join(' ') }}'" - when: "'docker_additional_registries' in openshift.common and docker_check.stat.isreg" - with_items: - - reg_conf_var: ADD_REGISTRY - reg_fact_val: "{{ openshift.common.docker_additional_registries }}" - reg_flag: --add-registry - - reg_conf_var: BLOCK_REGISTRY - reg_fact_val: "{{ openshift.common.docker_blocked_registries }}" - reg_flag: --block-registry - - reg_conf_var: INSECURE_REGISTRY - reg_fact_val: "{{ openshift.common.docker_insecure_registries }}" - reg_flag: --insecure-registry - notify: - - restart openshift_docker - -# TODO: Enable secure registry when code available in origin -# TODO: perhaps move this to openshift_docker? -- name: Secure Registry and Logs Options - lineinfile: - dest: /etc/sysconfig/docker - regexp: '^OPTIONS=.*$' - line: "OPTIONS='--insecure-registry={{ openshift.node.portal_net }}\ - {% if ansible_selinux and ansible_selinux.status == '''enabled''' %} --selinux-enabled{% endif %}\ - {% if openshift.node.docker_log_driver is defined %} --log-driver {{ openshift.node.docker_log_driver }}{% endif %}\ - {% if openshift.node.docker_log_options is defined %} {{ openshift.node.docker_log_options | oo_split() | oo_prepend_strings_in_list('--log-opt ') | join(' ')}}{% endif %}\ - {% if openshift.common.docker_options is defined %} {{ openshift.common.docker_options }}{% endif %}'" - when: docker_check.stat.isreg - notify: - - restart openshift_docker diff --git a/roles/openshift_docker_facts/meta/main.yml b/roles/openshift_docker_facts/meta/main.yml new file mode 100644 index 000000000..5b1be7a8d --- /dev/null +++ b/roles/openshift_docker_facts/meta/main.yml @@ -0,0 +1,15 @@ +--- +galaxy_info: + author: Jason DeTiberus + description: OpenShift Docker Facts + company: Red Hat, Inc. + license: Apache License, Version 2.0 + min_ansible_version: 1.9 + platforms: + - name: EL + versions: + - 7 + categories: + - cloud +dependencies: +- { role: openshift_facts } diff --git a/roles/openshift_docker_facts/tasks/main.yml b/roles/openshift_docker_facts/tasks/main.yml new file mode 100644 index 000000000..1848619e0 --- /dev/null +++ b/roles/openshift_docker_facts/tasks/main.yml @@ -0,0 +1,55 @@ +--- +- name: Set docker facts + openshift_facts: + role: "{{ item.role }}" + local_facts: "{{ item.local_facts }}" + with_items: + - role: docker + local_facts: + additional_registries: "{{ openshift_docker_additional_registries | default(None) }}" + blocked_registries: "{{ openshift_docker_blocked_registries | default(None) }}" + insecure_registries: "{{ openshift_docker_insecure_registries | default(None) }}" + log_driver: "{{ openshift_docker_log_driver | default(None) }}" + log_options: "{{ openshift_docker_log_options | default(None) }}" + options: "{{ openshift_docker_options | default(None) }}" + disable_push_dockerhub: "{{ openshift_disable_push_dockerhub | default(None) }}" + hosted_registry_insecure: "{{ openshift_docker_hosted_registry_insecure | default(None) }}" + hosted_registry_network: "{{ openshift_docker_hosted_registry_network | default(None) }}" + +- set_fact: + docker_additional_registries: "{{ openshift.docker.additional_registries + | default(omit) }}" + docker_blocked_registries: "{{ openshift.docker.blocked_registries + | default(omit) }}" + docker_insecure_registries: "{{ openshift.docker.insecure_registries + | default(omit) }}" + docker_log_driver: "{{ openshift.docker.log_driver | default(omit) }}" + docker_log_options: "{{ openshift.docker.log_options | default(omit) }}" + docker_push_dockerhub: "{{ openshift.docker.disable_push_dockerhub + | default(omit) }}" + +- set_fact: + docker_options: > + --insecure-registry={{ openshift.docker.hosted_registry_network }} + {{ openshift.docker.options | default ('') }} + when: openshift.docker.hosted_registry_insecure | default(False) | bool + +- set_fact: + docker_options: "{{ openshift.docker.options | default(omit) }}" + when: not openshift.docker.hosted_registry_insecure | default(False) | bool + +# Avoid docker 1.9 when installing origin < 1.2 or OSE < 3.2 on RHEL/Centos and +# See: https://bugzilla.redhat.com/show_bug.cgi?id=1304038 +- name: Gather common package version + command: > + {{ repoquery_cmd }} --qf '%{version}' "{{ openshift.common.service_type}}" + register: common_version + failed_when: false + changed_when: false + when: not openshift.common.is_atomic | bool + +- name: Set docker version to be installed + set_fact: + docker_version: "{{ '1.8.2' }}" + when: " ( common_version.stdout | default('0.0', True) | version_compare('3.2','<') and openshift.common.service_type == 'atomic-openshift' ) or + ( common_version.stdout | default('0.0', True) | version_compare('1.2','<') and openshift.common.service_type == 'origin' )" diff --git a/roles/openshift_docker_facts/vars/main.yml b/roles/openshift_docker_facts/vars/main.yml new file mode 100644 index 000000000..f7ad1b329 --- /dev/null +++ b/roles/openshift_docker_facts/vars/main.yml @@ -0,0 +1,2 @@ +--- +repoquery_cmd: "{{ 'dnf repoquery --latest-limit 1 -d 0' if ansible_pkg_mgr == 'dnf' else 'repoquery' }}" diff --git a/roles/openshift_etcd/meta/main.yml b/roles/openshift_etcd/meta/main.yml new file mode 100644 index 000000000..5e5f96d44 --- /dev/null +++ b/roles/openshift_etcd/meta/main.yml @@ -0,0 +1,18 @@ +--- +galaxy_info: + author: Jason DeTiberus + description: OpenShift etcd + company: Red Hat, Inc. + license: Apache License, Version 2.0 + min_ansible_version: 1.9 + platforms: + - name: EL + versions: + - 7 + categories: + - cloud +dependencies: +- role: openshift_facts +- role: openshift_docker + when: openshift.common.is_containerized | bool +- role: etcd diff --git a/roles/openshift_examples/defaults/main.yml b/roles/openshift_examples/defaults/main.yml index ad21d0f09..976ff7702 100644 --- a/roles/openshift_examples/defaults/main.yml +++ b/roles/openshift_examples/defaults/main.yml @@ -8,7 +8,7 @@ openshift_examples_load_quickstarts: true content_version: "{{ openshift.common.examples_content_version }}" -examples_base: "{{ openshift.common.config_base if openshift.common.is_containerized else '/usr/share/openshift' }}/examples" +examples_base: "{{ openshift.common.config_base if openshift.common.is_containerized | bool else '/usr/share/openshift' }}/examples" image_streams_base: "{{ examples_base }}/image-streams" centos_image_streams: "{{ image_streams_base}}/image-streams-centos7.json" rhel_image_streams: "{{ image_streams_base}}/image-streams-rhel7.json" diff --git a/roles/openshift_examples/examples-sync.sh b/roles/openshift_examples/examples-sync.sh index 22cef303b..6108a1485 100755 --- a/roles/openshift_examples/examples-sync.sh +++ b/roles/openshift_examples/examples-sync.sh @@ -6,7 +6,7 @@ # This script should be run from openshift-ansible/roles/openshift_examples XPAAS_VERSION=ose-v1.2.0-1 -ORIGIN_VERSION=v1.2 +ORIGIN_VERSION=${1:-v1.2} EXAMPLES_BASE=$(pwd)/files/examples/${ORIGIN_VERSION} find ${EXAMPLES_BASE} -name '*.json' -delete find ${EXAMPLES_BASE} -name '*.yaml' -delete diff --git a/roles/openshift_examples/files/examples/v1.1/db-templates/README.md b/roles/openshift_examples/files/examples/v1.1/db-templates/README.md index b39abf8b9..609f4dec9 100644 --- a/roles/openshift_examples/files/examples/v1.1/db-templates/README.md +++ b/roles/openshift_examples/files/examples/v1.1/db-templates/README.md @@ -9,7 +9,7 @@ the Web Console or the CLI. The examples can also be tweaked to create new templates. -## Ephemeral x Persistent +## Ephemeral vs Persistent For each supported database, there are two template files. diff --git a/roles/openshift_examples/files/examples/v1.1/db-templates/mongodb-ephemeral-template.json b/roles/openshift_examples/files/examples/v1.1/db-templates/mongodb-ephemeral-template.json index 68438b538..227c8d30e 100644 --- a/roles/openshift_examples/files/examples/v1.1/db-templates/mongodb-ephemeral-template.json +++ b/roles/openshift_examples/files/examples/v1.1/db-templates/mongodb-ephemeral-template.json @@ -48,8 +48,7 @@ }, "spec": { "strategy": { - "type": "Recreate", - "resources": {} + "type": "Recreate" }, "triggers": [ { @@ -62,7 +61,7 @@ "from": { "kind": "ImageStreamTag", "name": "mongodb:latest", - "namespace": "openshift" + "namespace": "${NAMESPACE}" }, "lastTriggeredImage": "" } @@ -87,19 +86,26 @@ { "name": "mongodb", "image": "mongodb", - "readinessProbe": { - "tcpSocket":{ - "port": 27017 - }, - "initialDelaySeconds": 15, - "timeoutSeconds": 1 - }, "ports": [ { "containerPort": 27017, "protocol": "TCP" } ], + "readinessProbe": { + "timeoutSeconds": 1, + "initialDelaySeconds": 3, + "exec": { + "command": [ "/bin/sh", "-i", "-c", "mongostat --host 127.0.0.1 -u admin -p $MONGODB_ADMIN_PASSWORD -n 1 --noheaders"] + } + }, + "livenessProbe": { + "timeoutSeconds": 1, + "initialDelaySeconds": 30, + "tcpSocket": { + "port": 27017 + } + }, "env": [ { "name": "MONGODB_USER", @@ -118,7 +124,11 @@ "value": "${MONGODB_ADMIN_PASSWORD}" } ], - "resources": {}, + "resources": { + "limits": { + "memory": "${MEMORY_LIMIT}" + } + }, "volumeMounts": [ { "name": "${DATABASE_SERVICE_NAME}-data", @@ -152,34 +162,51 @@ ], "parameters": [ { + "name": "MEMORY_LIMIT", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the container can use.", + "value": "512Mi" + }, + { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { "name": "DATABASE_SERVICE_NAME", - "description": "Database service name", + "displayName": "Database Service Name", + "description": "The name of the OpenShift Service exposed for the database.", "value": "mongodb", "required": true }, { "name": "MONGODB_USER", - "description": "Username for MongoDB user that will be used for accessing the database", + "displayName": "MongoDB User", + "description": "Username for MongoDB user that will be used for accessing the database.", "generate": "expression", "from": "user[A-Z0-9]{3}", "required": true }, { "name": "MONGODB_PASSWORD", - "description": "Password for the MongoDB user", + "displayName": "MongoDB Password", + "description": "Password for the MongoDB user.", "generate": "expression", "from": "[a-zA-Z0-9]{16}", "required": true }, { "name": "MONGODB_DATABASE", - "description": "Database name", + "displayName": "MongoDB Database Name", + "description": "Name of the MongoDB database accessed.", "value": "sampledb", "required": true }, { "name": "MONGODB_ADMIN_PASSWORD", - "description": "Password for the database admin user", + "displayName": "MongoDB Admin Password", + "description": "Password for the database admin user.", "generate": "expression", "from": "[a-zA-Z0-9]{16}", "required": true diff --git a/roles/openshift_examples/files/examples/v1.1/db-templates/mongodb-persistent-template.json b/roles/openshift_examples/files/examples/v1.1/db-templates/mongodb-persistent-template.json index e90ed6fa8..672eaaa09 100644 --- a/roles/openshift_examples/files/examples/v1.1/db-templates/mongodb-persistent-template.json +++ b/roles/openshift_examples/files/examples/v1.1/db-templates/mongodb-persistent-template.json @@ -65,8 +65,7 @@ }, "spec": { "strategy": { - "type": "Recreate", - "resources": {} + "type": "Recreate" }, "triggers": [ { @@ -79,7 +78,7 @@ "from": { "kind": "ImageStreamTag", "name": "mongodb:latest", - "namespace": "openshift" + "namespace": "${NAMESPACE}" }, "lastTriggeredImage": "" } @@ -104,19 +103,26 @@ { "name": "mongodb", "image": "mongodb", - "readinessProbe": { - "tcpSocket":{ - "port": 27017 - }, - "initialDelaySeconds": 15, - "timeoutSeconds": 1 - }, "ports": [ { "containerPort": 27017, "protocol": "TCP" } ], + "readinessProbe": { + "timeoutSeconds": 1, + "initialDelaySeconds": 3, + "exec": { + "command": [ "/bin/sh", "-i", "-c", "mongostat --host 127.0.0.1 -u admin -p $MONGODB_ADMIN_PASSWORD -n 1 --noheaders"] + } + }, + "livenessProbe": { + "timeoutSeconds": 1, + "initialDelaySeconds": 30, + "tcpSocket": { + "port": 27017 + } + }, "env": [ { "name": "MONGODB_USER", @@ -135,7 +141,11 @@ "value": "${MONGODB_ADMIN_PASSWORD}" } ], - "resources": {}, + "resources": { + "limits": { + "memory": "${MEMORY_LIMIT}" + } + }, "volumeMounts": [ { "name": "${DATABASE_SERVICE_NAME}-data", @@ -169,41 +179,59 @@ ], "parameters": [ { + "name": "MEMORY_LIMIT", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the container can use.", + "value": "512Mi" + }, + { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { "name": "DATABASE_SERVICE_NAME", - "description": "Database service name", + "displayName": "Database Service Name", + "description": "The name of the OpenShift Service exposed for the database.", "value": "mongodb", "required": true }, { "name": "MONGODB_USER", - "description": "Username for MongoDB user that will be used for accessing the database", + "displayName": "MongoDB User", + "description": "Username for MongoDB user that will be used for accessing the database.", "generate": "expression", "from": "user[A-Z0-9]{3}", "required": true }, { "name": "MONGODB_PASSWORD", - "description": "Password for the MongoDB user", + "displayName": "MongoDB Password", + "description": "Password for the MongoDB user.", "generate": "expression", "from": "[a-zA-Z0-9]{16}", "required": true }, { "name": "MONGODB_DATABASE", - "description": "Database name", + "displayName": "MongoDB Database Name", + "description": "Name of the MongoDB database accessed.", "value": "sampledb", "required": true }, { "name": "MONGODB_ADMIN_PASSWORD", - "description": "Password for the database admin user", + "displayName": "MongoDB Admin Password", + "description": "Password for the database admin user.", "generate": "expression", "from": "[a-zA-Z0-9]{16}", "required": true }, { "name": "VOLUME_CAPACITY", - "description": "Volume space available for data, e.g. 512Mi, 2Gi", + "displayName": "Volume Capacity", + "description": "Volume space available for data, e.g. 512Mi, 2Gi.", "value": "512Mi", "required": true } diff --git a/roles/openshift_examples/files/examples/v1.1/db-templates/mysql-ephemeral-template.json b/roles/openshift_examples/files/examples/v1.1/db-templates/mysql-ephemeral-template.json index 84911d2d6..f4c118052 100644 --- a/roles/openshift_examples/files/examples/v1.1/db-templates/mysql-ephemeral-template.json +++ b/roles/openshift_examples/files/examples/v1.1/db-templates/mysql-ephemeral-template.json @@ -48,8 +48,7 @@ }, "spec": { "strategy": { - "type": "Recreate", - "resources": {} + "type": "Recreate" }, "triggers": [ { @@ -62,7 +61,7 @@ "from": { "kind": "ImageStreamTag", "name": "mysql:latest", - "namespace": "openshift" + "namespace": "${NAMESPACE}" }, "lastTriggeredImage": "" } @@ -93,6 +92,21 @@ "protocol": "TCP" } ], + "readinessProbe": { + "timeoutSeconds": 1, + "initialDelaySeconds": 5, + "exec": { + "command": [ "/bin/sh", "-i", "-c", + "MYSQL_PWD=\"$MYSQL_PASSWORD\" mysql -h 127.0.0.1 -u $MYSQL_USER -D $MYSQL_DATABASE -e 'SELECT 1'"] + } + }, + "livenessProbe": { + "timeoutSeconds": 1, + "initialDelaySeconds": 30, + "tcpSocket": { + "port": 3306 + } + }, "env": [ { "name": "MYSQL_USER", @@ -107,7 +121,11 @@ "value": "${MYSQL_DATABASE}" } ], - "resources": {}, + "resources": { + "limits": { + "memory": "${MEMORY_LIMIT}" + } + }, "volumeMounts": [ { "name": "${DATABASE_SERVICE_NAME}-data", @@ -141,28 +159,44 @@ ], "parameters": [ { + "name": "MEMORY_LIMIT", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the container can use.", + "value": "512Mi" + }, + { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { "name": "DATABASE_SERVICE_NAME", - "description": "Database service name", + "displayName": "Database Service Name", + "description": "The name of the OpenShift Service exposed for the database.", "value": "mysql", "required": true }, { "name": "MYSQL_USER", - "description": "Username for MySQL user that will be used for accessing the database", + "displayName": "MySQL User", + "description": "Username for MySQL user that will be used for accessing the database.", "generate": "expression", "from": "user[A-Z0-9]{3}", "required": true }, { "name": "MYSQL_PASSWORD", - "description": "Password for the MySQL user", + "displayName": "MySQL Password", + "description": "Password for the MySQL user.", "generate": "expression", "from": "[a-zA-Z0-9]{16}", "required": true }, { "name": "MYSQL_DATABASE", - "description": "Database name", + "displayName": "MySQL Database Name", + "description": "Name of the MySQL database accessed.", "value": "sampledb", "required": true } diff --git a/roles/openshift_examples/files/examples/v1.1/db-templates/mysql-persistent-template.json b/roles/openshift_examples/files/examples/v1.1/db-templates/mysql-persistent-template.json index 6e19f48f5..d94262dde 100644 --- a/roles/openshift_examples/files/examples/v1.1/db-templates/mysql-persistent-template.json +++ b/roles/openshift_examples/files/examples/v1.1/db-templates/mysql-persistent-template.json @@ -65,8 +65,7 @@ }, "spec": { "strategy": { - "type": "Recreate", - "resources": {} + "type": "Recreate" }, "triggers": [ { @@ -79,7 +78,7 @@ "from": { "kind": "ImageStreamTag", "name": "mysql:latest", - "namespace": "openshift" + "namespace": "${NAMESPACE}" }, "lastTriggeredImage": "" } @@ -110,6 +109,21 @@ "protocol": "TCP" } ], + "readinessProbe": { + "timeoutSeconds": 1, + "initialDelaySeconds": 5, + "exec": { + "command": [ "/bin/sh", "-i", "-c", + "MYSQL_PWD=\"$MYSQL_PASSWORD\" mysql -h 127.0.0.1 -u $MYSQL_USER -D $MYSQL_DATABASE -e 'SELECT 1'"] + } + }, + "livenessProbe": { + "timeoutSeconds": 1, + "initialDelaySeconds": 30, + "tcpSocket": { + "port": 3306 + } + }, "env": [ { "name": "MYSQL_USER", @@ -124,7 +138,11 @@ "value": "${MYSQL_DATABASE}" } ], - "resources": {}, + "resources": { + "limits": { + "memory": "${MEMORY_LIMIT}" + } + }, "volumeMounts": [ { "name": "${DATABASE_SERVICE_NAME}-data", @@ -158,34 +176,51 @@ ], "parameters": [ { + "name": "MEMORY_LIMIT", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the container can use.", + "value": "512Mi" + }, + { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { "name": "DATABASE_SERVICE_NAME", - "description": "Database service name", + "displayName": "Database Service Name", + "description": "The name of the OpenShift Service exposed for the database.", "value": "mysql", "required": true }, { "name": "MYSQL_USER", - "description": "Username for MySQL user that will be used for accessing the database", + "displayName": "MySQL User", + "description": "Username for MySQL user that will be used for accessing the database.", "generate": "expression", "from": "user[A-Z0-9]{3}", "required": true }, { "name": "MYSQL_PASSWORD", - "description": "Password for the MySQL user", + "displayName": "MySQL Password", + "description": "Password for the MySQL user.", "generate": "expression", "from": "[a-zA-Z0-9]{16}", "required": true }, { "name": "MYSQL_DATABASE", - "description": "Database name", + "displayName": "MySQL Database Name", + "description": "Name of the MySQL database accessed.", "value": "sampledb", "required": true }, { "name": "VOLUME_CAPACITY", - "description": "Volume space available for data, e.g. 512Mi, 2Gi", + "displayName": "Volume Capacity", + "description": "Volume space available for data, e.g. 512Mi, 2Gi.", "value": "512Mi", "required": true } diff --git a/roles/openshift_examples/files/examples/v1.1/db-templates/postgresql-ephemeral-template.json b/roles/openshift_examples/files/examples/v1.1/db-templates/postgresql-ephemeral-template.json index 9ee9364a9..c14f3c3df 100644 --- a/roles/openshift_examples/files/examples/v1.1/db-templates/postgresql-ephemeral-template.json +++ b/roles/openshift_examples/files/examples/v1.1/db-templates/postgresql-ephemeral-template.json @@ -48,8 +48,7 @@ }, "spec": { "strategy": { - "type": "Recreate", - "resources": {} + "type": "Recreate" }, "triggers": [ { @@ -62,7 +61,7 @@ "from": { "kind": "ImageStreamTag", "name": "postgresql:latest", - "namespace": "openshift" + "namespace": "${NAMESPACE}" }, "lastTriggeredImage": "" } @@ -93,6 +92,20 @@ "protocol": "TCP" } ], + "readinessProbe": { + "timeoutSeconds": 1, + "initialDelaySeconds": 5, + "exec": { + "command": [ "/bin/sh", "-i", "-c", "psql -h 127.0.0.1 -U $POSTGRESQL_USER -q -d $POSTGRESQL_DATABASE -c 'SELECT 1'"] + } + }, + "livenessProbe": { + "timeoutSeconds": 1, + "initialDelaySeconds": 30, + "tcpSocket": { + "port": 5432 + } + }, "env": [ { "name": "POSTGRESQL_USER", @@ -107,7 +120,11 @@ "value": "${POSTGRESQL_DATABASE}" } ], - "resources": {}, + "resources": { + "limits": { + "memory": "${MEMORY_LIMIT}" + } + }, "volumeMounts": [ { "name": "${DATABASE_SERVICE_NAME}-data", @@ -141,28 +158,44 @@ ], "parameters": [ { + "name": "MEMORY_LIMIT", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the container can use.", + "value": "512Mi" + }, + { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { "name": "DATABASE_SERVICE_NAME", - "description": "Database service name", + "displayName": "Database Service Name", + "description": "The name of the OpenShift Service exposed for the database.", "value": "postgresql", "required": true }, { "name": "POSTGRESQL_USER", - "description": "Username for PostgreSQL user that will be used for accessing the database", + "displayName": "PostgreSQL User", + "description": "Username for PostgreSQL user that will be used for accessing the database.", "generate": "expression", "from": "user[A-Z0-9]{3}", "required": true }, { "name": "POSTGRESQL_PASSWORD", - "description": "Password for the PostgreSQL user", + "displayName": "PostgreSQL Password", + "description": "Password for the PostgreSQL user.", "generate": "expression", "from": "[a-zA-Z0-9]{16}", "required": true }, { "name": "POSTGRESQL_DATABASE", - "description": "Database name", + "displayName": "PostgreSQL Database Name", + "description": "Name of the PostgreSQL database accessed.", "value": "sampledb", "required": true } diff --git a/roles/openshift_examples/files/examples/v1.1/db-templates/postgresql-persistent-template.json b/roles/openshift_examples/files/examples/v1.1/db-templates/postgresql-persistent-template.json index 91cd7453e..5713411ad 100644 --- a/roles/openshift_examples/files/examples/v1.1/db-templates/postgresql-persistent-template.json +++ b/roles/openshift_examples/files/examples/v1.1/db-templates/postgresql-persistent-template.json @@ -65,8 +65,7 @@ }, "spec": { "strategy": { - "type": "Recreate", - "resources": {} + "type": "Recreate" }, "triggers": [ { @@ -79,7 +78,7 @@ "from": { "kind": "ImageStreamTag", "name": "postgresql:latest", - "namespace": "openshift" + "namespace": "${NAMESPACE}" }, "lastTriggeredImage": "" } @@ -110,6 +109,20 @@ "protocol": "TCP" } ], + "readinessProbe": { + "timeoutSeconds": 1, + "initialDelaySeconds": 5, + "exec": { + "command": [ "/bin/sh", "-i", "-c", "psql -h 127.0.0.1 -U $POSTGRESQL_USER -q -d $POSTGRESQL_DATABASE -c 'SELECT 1'"] + } + }, + "livenessProbe": { + "timeoutSeconds": 1, + "initialDelaySeconds": 30, + "tcpSocket": { + "port": 5432 + } + }, "env": [ { "name": "POSTGRESQL_USER", @@ -124,7 +137,11 @@ "value": "${POSTGRESQL_DATABASE}" } ], - "resources": {}, + "resources": { + "limits": { + "memory": "${MEMORY_LIMIT}" + } + }, "volumeMounts": [ { "name": "${DATABASE_SERVICE_NAME}-data", @@ -158,34 +175,51 @@ ], "parameters": [ { + "name": "MEMORY_LIMIT", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the container can use.", + "value": "512Mi" + }, + { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { "name": "DATABASE_SERVICE_NAME", - "description": "Database service name", + "displayName": "Database Service Name", + "description": "The name of the OpenShift Service exposed for the database.", "value": "postgresql", "required": true }, { "name": "POSTGRESQL_USER", - "description": "Username for PostgreSQL user that will be used for accessing the database", + "displayName": "PostgreSQL User", + "description": "Username for PostgreSQL user that will be used for accessing the database.", "generate": "expression", "from": "user[A-Z0-9]{3}", "required": true }, { "name": "POSTGRESQL_PASSWORD", - "description": "Password for the PostgreSQL user", + "displayName": "PostgreSQL Password", + "description": "Password for the PostgreSQL user.", "generate": "expression", "from": "[a-zA-Z0-9]{16}", "required": true }, { "name": "POSTGRESQL_DATABASE", - "description": "Database name", + "displayName": "PostgreSQL Database Name", + "description": "Name of the PostgreSQL database accessed.", "value": "sampledb", "required": true }, { "name": "VOLUME_CAPACITY", - "description": "Volume space available for data, e.g. 512Mi, 2Gi", + "displayName": "Volume Capacity", + "description": "Volume space available for data, e.g. 512Mi, 2Gi.", "value": "512Mi", "required": true } diff --git a/roles/openshift_examples/files/examples/v1.1/image-streams/image-streams-centos7.json b/roles/openshift_examples/files/examples/v1.1/image-streams/image-streams-centos7.json index a327c0215..719aee772 100644 --- a/roles/openshift_examples/files/examples/v1.1/image-streams/image-streams-centos7.json +++ b/roles/openshift_examples/files/examples/v1.1/image-streams/image-streams-centos7.json @@ -14,6 +14,13 @@ "tags": [ { "name": "latest", + "annotations": { + "description": "Build and run Ruby applications", + "iconClass": "icon-ruby", + "tags": "builder,ruby", + "supports": "ruby", + "sampleRepo": "https://github.com/openshift/ruby-ex.git" + }, "from": { "kind": "ImageStreamTag", "name": "2.2" @@ -63,6 +70,13 @@ "tags": [ { "name": "latest", + "annotations": { + "description": "Build and run NodeJS applications", + "iconClass": "icon-nodejs", + "tags": "builder,nodejs", + "supports":"nodejs", + "sampleRepo": "https://github.com/openshift/nodejs-ex.git" + }, "from": { "kind": "ImageStreamTag", "name": "0.10" @@ -97,6 +111,13 @@ "tags": [ { "name": "latest", + "annotations": { + "description": "Build and run Perl applications", + "iconClass": "icon-perl", + "tags": "builder,perl", + "supports":"perl", + "sampleRepo": "https://github.com/openshift/dancer-ex.git" + }, "from": { "kind": "ImageStreamTag", "name": "5.20" @@ -147,6 +168,13 @@ "tags": [ { "name": "latest", + "annotations": { + "description": "Build and run PHP applications", + "iconClass": "icon-php", + "tags": "builder,php", + "supports":"php", + "sampleRepo": "https://github.com/openshift/cakephp-ex.git" + }, "from": { "kind": "ImageStreamTag", "name": "5.6" @@ -196,6 +224,13 @@ "tags": [ { "name": "latest", + "annotations": { + "description": "Build and run Python applications", + "iconClass": "icon-python", + "tags": "builder,python", + "supports":"python", + "sampleRepo": "https://github.com/openshift/django-ex.git" + }, "from": { "kind": "ImageStreamTag", "name": "3.4" @@ -260,9 +295,16 @@ "tags": [ { "name": "latest", + "annotations": { + "description": "Build and run Java applications on Wildfly", + "iconClass": "icon-wildfly", + "tags": "builder,wildfly,java", + "supports":"jee,java", + "sampleRepo": "https://github.com/bparees/openshift-jee-sample.git" + }, "from": { "kind": "ImageStreamTag", - "name": "8.1" + "name": "10.0" } }, { @@ -279,6 +321,36 @@ "kind": "DockerImage", "name": "openshift/wildfly-81-centos7:latest" } + }, + { + "name": "9.0", + "annotations": { + "description": "Build and run Java applications on Wildfly 9.0", + "iconClass": "icon-wildfly", + "tags": "builder,wildfly,java", + "supports":"wildfly:9.0,jee,java", + "version": "9.0", + "sampleRepo": "https://github.com/bparees/openshift-jee-sample.git" + }, + "from": { + "kind": "DockerImage", + "name": "openshift/wildfly-90-centos7:latest" + } + }, + { + "name": "10.0", + "annotations": { + "description": "Build and run Java applications on Wildfly 10.0", + "iconClass": "icon-wildfly", + "tags": "builder,wildfly,java", + "supports":"wildfly:10.0,jee,java", + "version": "10.0", + "sampleRepo": "https://github.com/bparees/openshift-jee-sample.git" + }, + "from": { + "kind": "DockerImage", + "name": "openshift/wildfly-100-centos7:latest" + } } ] } @@ -294,6 +366,11 @@ "tags": [ { "name": "latest", + "annotations": { + "description": "Provides a MySQL database", + "iconClass": "icon-mysql-database", + "tags": "mysql" + }, "from": { "kind": "ImageStreamTag", "name": "5.6" @@ -301,6 +378,12 @@ }, { "name": "5.5", + "annotations": { + "description": "Provides a MySQL v5.5 database", + "iconClass": "icon-mysql-database", + "tags": "mysql", + "version": "5.5" + }, "from": { "kind": "DockerImage", "name": "openshift/mysql-55-centos7:latest" @@ -308,6 +391,12 @@ }, { "name": "5.6", + "annotations": { + "description": "Provides a MySQL v5.6 database", + "iconClass": "icon-mysql-database", + "tags": "mysql", + "version": "5.6" + }, "from": { "kind": "DockerImage", "name": "centos/mysql-56-centos7:latest" @@ -327,6 +416,11 @@ "tags": [ { "name": "latest", + "annotations": { + "description": "Provides a PostgreSQL database", + "iconClass": "icon-postgresql", + "tags": "postgresql" + }, "from": { "kind": "ImageStreamTag", "name": "9.4" @@ -334,6 +428,12 @@ }, { "name": "9.2", + "annotations": { + "description": "Provides a PostgreSQL v9.2 database", + "iconClass": "icon-postgresql", + "tags": "postgresql", + "version": "9.2" + }, "from": { "kind": "DockerImage", "name": "openshift/postgresql-92-centos7:latest" @@ -341,6 +441,12 @@ }, { "name": "9.4", + "annotations": { + "description": "Provides a PostgreSQL v9.4 database", + "iconClass": "icon-postgresql", + "tags": "postgresql", + "version": "9.4" + }, "from": { "kind": "DockerImage", "name": "centos/postgresql-94-centos7:latest" @@ -360,6 +466,11 @@ "tags": [ { "name": "latest", + "annotations": { + "description": "Provides a MongoDB database", + "iconClass": "icon-mongodb", + "tags": "mongodb" + }, "from": { "kind": "ImageStreamTag", "name": "2.6" @@ -367,6 +478,12 @@ }, { "name": "2.4", + "annotations": { + "description": "Provides a MongoDB v2.4 database", + "iconClass": "icon-mongodb", + "tags": "mongodb", + "version": "2.4" + }, "from": { "kind": "DockerImage", "name": "openshift/mongodb-24-centos7:latest" @@ -374,6 +491,12 @@ }, { "name": "2.6", + "annotations": { + "description": "Provides a MongoDB v2.6 database", + "iconClass": "icon-mongodb", + "tags": "mongodb", + "version": "2.6" + }, "from": { "kind": "DockerImage", "name": "centos/mongodb-26-centos7:latest" @@ -393,6 +516,11 @@ "tags": [ { "name": "latest", + "annotations": { + "description": "Provides a Jenkins server", + "iconClass": "icon-jenkins", + "tags": "jenkins" + }, "from": { "kind": "ImageStreamTag", "name": "1" @@ -400,6 +528,12 @@ }, { "name": "1", + "annotations": { + "description": "Provides a Jenkins server", + "iconClass": "icon-jenkins", + "tags": "jenkins", + "version": "1.x" + }, "from": { "kind": "DockerImage", "name": "openshift/jenkins-1-centos7:latest" diff --git a/roles/openshift_examples/files/examples/v1.1/image-streams/image-streams-rhel7.json b/roles/openshift_examples/files/examples/v1.1/image-streams/image-streams-rhel7.json index 3f5f713b4..00635ec78 100644 --- a/roles/openshift_examples/files/examples/v1.1/image-streams/image-streams-rhel7.json +++ b/roles/openshift_examples/files/examples/v1.1/image-streams/image-streams-rhel7.json @@ -14,6 +14,13 @@ "tags": [ { "name": "latest", + "annotations": { + "description": "Build and run Ruby applications", + "iconClass": "icon-ruby", + "tags": "builder,ruby", + "supports": "ruby,ruby", + "sampleRepo": "https://github.com/openshift/ruby-ex.git" + }, "from": { "kind": "ImageStreamTag", "name": "2.2" @@ -63,6 +70,13 @@ "tags": [ { "name": "latest", + "annotations": { + "description": "Build and run NodeJS applications", + "iconClass": "icon-nodejs", + "tags": "builder,nodejs", + "supports":"nodejs", + "sampleRepo": "https://github.com/openshift/nodejs-ex.git" + }, "from": { "kind": "ImageStreamTag", "name": "0.10" @@ -97,6 +111,13 @@ "tags": [ { "name": "latest", + "annotations": { + "description": "Build and run Perl applications", + "iconClass": "icon-perl", + "tags": "builder,perl", + "supports":"perl", + "sampleRepo": "https://github.com/openshift/dancer-ex.git" + }, "from": { "kind": "ImageStreamTag", "name": "5.20" @@ -147,6 +168,13 @@ "tags": [ { "name": "latest", + "annotations": { + "description": "Build and run PHP applications", + "iconClass": "icon-php", + "tags": "builder,php", + "supports":"php", + "sampleRepo": "https://github.com/openshift/cakephp-ex.git" + }, "from": { "kind": "ImageStreamTag", "name": "5.6" @@ -196,6 +224,13 @@ "tags": [ { "name": "latest", + "annotations": { + "description": "Build and run Python applications", + "iconClass": "icon-python", + "tags": "builder,python", + "supports":"python", + "sampleRepo": "https://github.com/openshift/django-ex.git" + }, "from": { "kind": "ImageStreamTag", "name": "3.4" @@ -260,6 +295,11 @@ "tags": [ { "name": "latest", + "annotations": { + "description": "Provides a MySQL database", + "iconClass": "icon-mysql-database", + "tags": "mysql" + }, "from": { "kind": "ImageStreamTag", "name": "5.6" @@ -267,6 +307,12 @@ }, { "name": "5.5", + "annotations": { + "description": "Provides a MySQL v5.5 database", + "iconClass": "icon-mysql-database", + "tags": "mysql", + "version": "5.5" + }, "from": { "kind": "DockerImage", "name": "registry.access.redhat.com/openshift3/mysql-55-rhel7:latest" @@ -274,6 +320,12 @@ }, { "name": "5.6", + "annotations": { + "description": "Provides a MySQL v5.6 database", + "iconClass": "icon-mysql-database", + "tags": "mysql", + "version": "5.6" + }, "from": { "kind": "DockerImage", "name": "registry.access.redhat.com/rhscl/mysql-56-rhel7:latest" @@ -293,6 +345,11 @@ "tags": [ { "name": "latest", + "annotations": { + "description": "Provides a PostgreSQL database", + "iconClass": "icon-postgresql", + "tags": "postgresql" + }, "from": { "kind": "ImageStreamTag", "name": "9.4" @@ -300,6 +357,12 @@ }, { "name": "9.2", + "annotations": { + "description": "Provides a PostgreSQL v9.2 database", + "iconClass": "icon-postgresql", + "tags": "postgresql", + "version": "9.2" + }, "from": { "kind": "DockerImage", "name": "registry.access.redhat.com/openshift3/postgresql-92-rhel7:latest" @@ -307,6 +370,12 @@ }, { "name": "9.4", + "annotations": { + "description": "Provides a PostgreSQL v9.4 database", + "iconClass": "icon-postgresql", + "tags": "postgresql", + "version": "9.4" + }, "from": { "kind": "DockerImage", "name": "registry.access.redhat.com/rhscl/postgresql-94-rhel7:latest" @@ -326,6 +395,11 @@ "tags": [ { "name": "latest", + "annotations": { + "description": "Provides a MongoDB database", + "iconClass": "icon-mongodb", + "tags": "mongodb" + }, "from": { "kind": "ImageStreamTag", "name": "2.6" @@ -333,6 +407,12 @@ }, { "name": "2.4", + "annotations": { + "description": "Provides a MongoDB v2.4 database", + "iconClass": "icon-mongodb", + "tags": "mongodb", + "version": "2.4" + }, "from": { "kind": "DockerImage", "name": "registry.access.redhat.com/openshift3/mongodb-24-rhel7:latest" @@ -340,6 +420,12 @@ }, { "name": "2.6", + "annotations": { + "description": "Provides a MongoDB v2.6 database", + "iconClass": "icon-mongodb", + "tags": "mongodb", + "version": "2.6" + }, "from": { "kind": "DockerImage", "name": "registry.access.redhat.com/rhscl/mongodb-26-rhel7:latest" @@ -359,6 +445,11 @@ "tags": [ { "name": "latest", + "annotations": { + "description": "Provides a Jenkins server", + "iconClass": "icon-jenkins", + "tags": "jenkins" + }, "from": { "kind": "ImageStreamTag", "name": "1" @@ -366,6 +457,12 @@ }, { "name": "1", + "annotations": { + "description": "Provides a Jenkins server", + "iconClass": "icon-jenkins", + "tags": "jenkins", + "version": "1.x" + }, "from": { "kind": "DockerImage", "name": "registry.access.redhat.com/openshift3/jenkins-1-rhel7:latest" diff --git a/roles/openshift_examples/files/examples/v1.1/infrastructure-templates/enterprise/metrics-deployer.yaml b/roles/openshift_examples/files/examples/v1.1/infrastructure-templates/enterprise/metrics-deployer.yaml index be999bd1a..99f2df4fa 100644 --- a/roles/openshift_examples/files/examples/v1.1/infrastructure-templates/enterprise/metrics-deployer.yaml +++ b/roles/openshift_examples/files/examples/v1.1/infrastructure-templates/enterprise/metrics-deployer.yaml @@ -52,8 +52,6 @@ objects: value: ${IMAGE_PREFIX} - name: IMAGE_VERSION value: ${IMAGE_VERSION} - - name: PUBLIC_MASTER_URL - value: ${PUBLIC_MASTER_URL} - name: MASTER_URL value: ${MASTER_URL} - name: REDEPLOY @@ -79,11 +77,11 @@ objects: secretName: metrics-deployer parameters: - - description: 'Specify prefix for metrics components; e.g. for "openshift/origin-metrics-deployer:v1.1", set prefix "openshift/origin-"' + description: 'Specify prefix for metrics components; e.g. for "openshift/origin-metrics-deployer:latest", set prefix "openshift/origin-"' name: IMAGE_PREFIX value: "registry.access.redhat.com/openshift3/" - - description: 'Specify version for metrics components; e.g. for "openshift/origin-metrics-deployer:v1.1", set version "v1.1"' + description: 'Specify version for metrics components; e.g. for "openshift/origin-metrics-deployer:latest", set version "latest"' name: IMAGE_VERSION value: "3.1.1" - @@ -109,7 +107,7 @@ parameters: - description: "The persistent volume size for each of the Cassandra nodes" name: CASSANDRA_PV_SIZE - value: "1Gi" + value: "10Gi" - description: "How many days metrics should be stored for." name: METRIC_DURATION diff --git a/roles/openshift_examples/files/examples/v1.1/infrastructure-templates/origin/logging-deployer.yaml b/roles/openshift_examples/files/examples/v1.1/infrastructure-templates/origin/logging-deployer.yaml index 4c798e148..9257b1f28 100644 --- a/roles/openshift_examples/files/examples/v1.1/infrastructure-templates/origin/logging-deployer.yaml +++ b/roles/openshift_examples/files/examples/v1.1/infrastructure-templates/origin/logging-deployer.yaml @@ -69,6 +69,8 @@ objects: value: ${ES_OPS_RECOVER_EXPECTED_NODES} - name: ES_OPS_RECOVER_AFTER_TIME value: ${ES_OPS_RECOVER_AFTER_TIME} + - name: FLUENTD_NODESELECTOR + value: ${FLUENTD_NODESELECTOR} dnsPolicy: ClusterFirst restartPolicy: Never serviceAccount: logging-deployer @@ -148,4 +150,7 @@ parameters: description: "Timeout for *expected* ops nodes to be present when cluster is recovering from a full restart." name: ES_OPS_RECOVER_AFTER_TIME value: "5m" - +- + description: "The nodeSelector used for the Fluentd DaemonSet." + name: FLUENTD_NODESELECTOR + value: "logging-infra-fluentd=true" diff --git a/roles/openshift_examples/files/examples/v1.1/infrastructure-templates/origin/metrics-deployer.yaml b/roles/openshift_examples/files/examples/v1.1/infrastructure-templates/origin/metrics-deployer.yaml index 3e9bcde5b..30d79acee 100644 --- a/roles/openshift_examples/files/examples/v1.1/infrastructure-templates/origin/metrics-deployer.yaml +++ b/roles/openshift_examples/files/examples/v1.1/infrastructure-templates/origin/metrics-deployer.yaml @@ -52,8 +52,6 @@ objects: value: ${IMAGE_PREFIX} - name: IMAGE_VERSION value: ${IMAGE_VERSION} - - name: PUBLIC_MASTER_URL - value: ${PUBLIC_MASTER_URL} - name: MASTER_URL value: ${MASTER_URL} - name: REDEPLOY @@ -79,11 +77,11 @@ objects: secretName: metrics-deployer parameters: - - description: 'Specify prefix for metrics components; e.g. for "openshift/origin-metrics-deployer:v1.1", set prefix "openshift/origin-"' + description: 'Specify prefix for metrics components; e.g. for "openshift/origin-metrics-deployer:latest", set prefix "openshift/origin-"' name: IMAGE_PREFIX - value: "docker.io/openshift/origin-" + value: "openshift/origin-" - - description: 'Specify version for metrics components; e.g. for "openshift/origin-metrics-deployer:v1.1", set version "v1.1"' + description: 'Specify version for metrics components; e.g. for "openshift/origin-metrics-deployer:latest", set version "latest"' name: IMAGE_VERSION value: "latest" - @@ -109,7 +107,7 @@ parameters: - description: "The persistent volume size for each of the Cassandra nodes" name: CASSANDRA_PV_SIZE - value: "1Gi" + value: "10Gi" - description: "How many days metrics should be stored for." name: METRIC_DURATION diff --git a/roles/openshift_examples/files/examples/v1.1/quickstart-templates/cakephp-mysql.json b/roles/openshift_examples/files/examples/v1.1/quickstart-templates/cakephp-mysql.json index 52143da2d..922e5bed8 100644 --- a/roles/openshift_examples/files/examples/v1.1/quickstart-templates/cakephp-mysql.json +++ b/roles/openshift_examples/files/examples/v1.1/quickstart-templates/cakephp-mysql.json @@ -5,7 +5,7 @@ "name": "cakephp-mysql-example", "annotations": { "description": "An example CakePHP application with a MySQL database", - "tags": "instant-app,php,cakephp,mysql", + "tags": "quickstart,php,cakephp,mysql", "iconClass": "icon-php" } }, @@ -17,7 +17,7 @@ "kind": "Service", "apiVersion": "v1", "metadata": { - "name": "cakephp-mysql-example", + "name": "${NAME}", "annotations": { "description": "Exposes and load balances the application pods" } @@ -31,7 +31,7 @@ } ], "selector": { - "name": "cakephp-mysql-example" + "name": "${NAME}" } } }, @@ -39,13 +39,13 @@ "kind": "Route", "apiVersion": "v1", "metadata": { - "name": "cakephp-mysql-example" + "name": "${NAME}" }, "spec": { "host": "${APPLICATION_DOMAIN}", "to": { "kind": "Service", - "name": "cakephp-mysql-example" + "name": "${NAME}" } } }, @@ -53,7 +53,7 @@ "kind": "ImageStream", "apiVersion": "v1", "metadata": { - "name": "cakephp-mysql-example", + "name": "${NAME}", "annotations": { "description": "Keeps track of changes in the application image" } @@ -63,7 +63,7 @@ "kind": "BuildConfig", "apiVersion": "v1", "metadata": { - "name": "cakephp-mysql-example", + "name": "${NAME}", "annotations": { "description": "Defines how to build the application" } @@ -82,7 +82,7 @@ "sourceStrategy": { "from": { "kind": "ImageStreamTag", - "namespace": "openshift", + "namespace": "${NAMESPACE}", "name": "php:5.6" } } @@ -90,7 +90,7 @@ "output": { "to": { "kind": "ImageStreamTag", - "name": "cakephp-mysql-example:latest" + "name": "${NAME}:latest" } }, "triggers": [ @@ -113,7 +113,7 @@ "kind": "DeploymentConfig", "apiVersion": "v1", "metadata": { - "name": "cakephp-mysql-example", + "name": "${NAME}", "annotations": { "description": "Defines how to deploy the application server" } @@ -123,7 +123,7 @@ "type": "Rolling", "recreateParams": { "pre": { - "failurePolicy": "Abort", + "failurePolicy": "Retry", "execNewPod": { "command": [ "./migrate-database.sh" @@ -143,7 +143,7 @@ ], "from": { "kind": "ImageStreamTag", - "name": "cakephp-mysql-example:latest" + "name": "${NAME}:latest" } } }, @@ -153,13 +153,13 @@ ], "replicas": 1, "selector": { - "name": "cakephp-mysql-example" + "name": "${NAME}" }, "template": { "metadata": { - "name": "cakephp-mysql-example", + "name": "${NAME}", "labels": { - "name": "cakephp-mysql-example" + "name": "${NAME}" } }, "spec": { @@ -172,6 +172,22 @@ "containerPort": 8080 } ], + "readinessProbe": { + "timeoutSeconds": 3, + "initialDelaySeconds": 3, + "httpGet": { + "path": "/health.php", + "port": 8080 + } + }, + "livenessProbe": { + "timeoutSeconds": 3, + "initialDelaySeconds": 30, + "httpGet": { + "path": "/", + "port": 8080 + } + }, "env": [ { "name": "DATABASE_SERVICE_NAME", @@ -209,7 +225,12 @@ "name": "OPCACHE_REVALIDATE_FREQ", "value": "${OPCACHE_REVALIDATE_FREQ}" } - ] + ], + "resources": { + "limits": { + "memory": "${MEMORY_LIMIT}" + } + } } ] } @@ -261,7 +282,7 @@ ], "from": { "kind": "ImageStreamTag", - "namespace": "openshift", + "namespace": "${NAMESPACE}", "name": "mysql:5.6" } } @@ -291,20 +312,39 @@ "containerPort": 3306 } ], - "env": [ - { - "name": "MYSQL_USER", - "value": "${DATABASE_USER}" - }, - { - "name": "MYSQL_PASSWORD", - "value": "${DATABASE_PASSWORD}" - }, - { - "name": "MYSQL_DATABASE", - "value": "${DATABASE_NAME}" + "readinessProbe": { + "timeoutSeconds": 1, + "initialDelaySeconds": 5, + "exec": { + "command": [ "/bin/sh", "-i", "-c", "MYSQL_PWD='${DATABASE_PASSWORD}' mysql -h 127.0.0.1 -u ${DATABASE_USER} -D ${DATABASE_NAME} -e 'SELECT 1'" ] } - ] + }, + "livenessProbe": { + "timeoutSeconds": 1, + "initialDelaySeconds": 30, + "tcpSocket": { + "port": 3306 + } + }, + "env": [ + { + "name": "MYSQL_USER", + "value": "${DATABASE_USER}" + }, + { + "name": "MYSQL_PASSWORD", + "value": "${DATABASE_PASSWORD}" + }, + { + "name": "MYSQL_DATABASE", + "value": "${DATABASE_NAME}" + } + ], + "resources": { + "limits": { + "memory": "${MEMORY_MYSQL_LIMIT}" + } + } } ] } @@ -314,76 +354,111 @@ ], "parameters": [ { + "name": "NAME", + "displayName": "Name", + "description": "The name assigned to all of the frontend objects defined in this template.", + "required": true, + "value": "cakephp-mysql-example" + }, + { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { + "name": "MEMORY_LIMIT", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the CakePHP container can use.", + "value": "512Mi" + }, + { + "name": "MEMORY_MYSQL_LIMIT", + "displayName": "Memory Limit (MySQL)", + "description": "Maximum amount of memory the MySQL container can use.", + "value": "512Mi" + }, + { "name": "SOURCE_REPOSITORY_URL", - "description": "The URL of the repository with your application source code", + "displayName": "Git Repository URL", + "description": "The URL of the repository with your application source code.", "value": "https://github.com/openshift/cakephp-ex.git" }, { "name": "SOURCE_REPOSITORY_REF", - "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch" + "displayName": "Git Reference", + "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch." }, { "name": "CONTEXT_DIR", - "description": "Set this to the relative path to your project if it is not in the root of your repository" + "displayName": "Context Directory", + "description": "Set this to the relative path to your project if it is not in the root of your repository." }, { "name": "APPLICATION_DOMAIN", + "displayName": "Application Hostname", "description": "The exposed hostname that will route to the CakePHP service, if left blank a value will be defaulted.", "value": "" }, { "name": "GITHUB_WEBHOOK_SECRET", - "description": "A secret string used to configure the GitHub webhook", + "displayName": "GitHub Webhook Secret", + "description": "A secret string used to configure the GitHub webhook.", "generate": "expression", "from": "[a-zA-Z0-9]{40}" }, { "name": "DATABASE_SERVICE_NAME", - "description": "Database service name", + "displayName": "Database Service Name", "value": "mysql" }, { "name": "DATABASE_ENGINE", - "description": "Database engine: postgresql, mysql or sqlite (default)", + "displayName": "Database Engine", + "description": "Database engine: postgresql, mysql or sqlite (default).", "value": "mysql" }, { "name": "DATABASE_NAME", - "description": "Database name", + "displayName": "Database Name", "value": "default" }, { "name": "DATABASE_USER", - "description": "Database user name", + "displayName": "Database User", "value": "cakephp" }, { "name": "DATABASE_PASSWORD", - "description": "Database user password", + "displayName": "Database Password", "generate": "expression", "from": "[a-zA-Z0-9]{16}" }, { "name": "CAKEPHP_SECRET_TOKEN", - "description": "Set this to a long random string", + "displayName": "CakePHP secret token", + "description": "Set this to a long random string.", "generate": "expression", "from": "[\\w]{50}" }, { "name": "CAKEPHP_SECURITY_SALT", - "description": "Security salt for session hash", + "displayName": "CakePHP Security Salt", + "description": "Security salt for session hash.", "generate": "expression", "from": "[a-zA-Z0-9]{40}" }, { "name": "CAKEPHP_SECURITY_CIPHER_SEED", - "description": "Security cipher seed for session hash", + "displayName": "CakePHP Security Cipher Seed", + "description": "Security cipher seed for session hash.", "generate": "expression", "from": "[0-9]{30}" }, { "name": "OPCACHE_REVALIDATE_FREQ", - "description": "The How often to check script timestamps for updates, in seconds. 0 will result in OPcache checking for updates on every request.", + "displayName": "OPcache Revalidation Frequency", + "description": "How often to check script timestamps for updates, in seconds. 0 will result in OPcache checking for updates on every request.", "value": "2" } ] diff --git a/roles/openshift_examples/files/examples/v1.1/quickstart-templates/cakephp.json b/roles/openshift_examples/files/examples/v1.1/quickstart-templates/cakephp.json index b77dc0c51..780faec55 100644 --- a/roles/openshift_examples/files/examples/v1.1/quickstart-templates/cakephp.json +++ b/roles/openshift_examples/files/examples/v1.1/quickstart-templates/cakephp.json @@ -5,7 +5,7 @@ "name": "cakephp-example", "annotations": { "description": "An example CakePHP application with no database", - "tags": "instant-app,php,cakephp", + "tags": "quickstart,php,cakephp", "iconClass": "icon-php" } }, @@ -17,7 +17,7 @@ "kind": "Service", "apiVersion": "v1", "metadata": { - "name": "cakephp-example", + "name": "${NAME}", "annotations": { "description": "Exposes and load balances the application pods" } @@ -31,7 +31,7 @@ } ], "selector": { - "name": "cakephp-example" + "name": "${NAME}" } } }, @@ -39,13 +39,13 @@ "kind": "Route", "apiVersion": "v1", "metadata": { - "name": "cakephp-example" + "name": "${NAME}" }, "spec": { "host": "${APPLICATION_DOMAIN}", "to": { "kind": "Service", - "name": "cakephp-example" + "name": "${NAME}" } } }, @@ -53,7 +53,7 @@ "kind": "ImageStream", "apiVersion": "v1", "metadata": { - "name": "cakephp-example", + "name": "${NAME}", "annotations": { "description": "Keeps track of changes in the application image" } @@ -63,7 +63,7 @@ "kind": "BuildConfig", "apiVersion": "v1", "metadata": { - "name": "cakephp-example", + "name": "${NAME}", "annotations": { "description": "Defines how to build the application" } @@ -82,7 +82,7 @@ "sourceStrategy": { "from": { "kind": "ImageStreamTag", - "namespace": "openshift", + "namespace": "${NAMESPACE}", "name": "php:5.6" } } @@ -90,7 +90,7 @@ "output": { "to": { "kind": "ImageStreamTag", - "name": "cakephp-example:latest" + "name": "${NAME}:latest" } }, "triggers": [ @@ -113,7 +113,7 @@ "kind": "DeploymentConfig", "apiVersion": "v1", "metadata": { - "name": "cakephp-example", + "name": "${NAME}", "annotations": { "description": "Defines how to deploy the application server" } @@ -132,7 +132,7 @@ ], "from": { "kind": "ImageStreamTag", - "name": "cakephp-example:latest" + "name": "${NAME}:latest" } } }, @@ -142,13 +142,13 @@ ], "replicas": 1, "selector": { - "name": "cakephp-example" + "name": "${NAME}" }, "template": { "metadata": { - "name": "cakephp-example", + "name": "${NAME}", "labels": { - "name": "cakephp-example" + "name": "${NAME}" } }, "spec": { @@ -161,6 +161,22 @@ "containerPort": 8080 } ], + "readinessProbe": { + "timeoutSeconds": 3, + "initialDelaySeconds": 3, + "httpGet": { + "path": "/", + "port": 8080 + } + }, + "livenessProbe": { + "timeoutSeconds": 3, + "initialDelaySeconds": 30, + "httpGet": { + "path": "/", + "port": 8080 + } + }, "env": [ { "name": "DATABASE_SERVICE_NAME", @@ -198,7 +214,12 @@ "name": "OPCACHE_REVALIDATE_FREQ", "value": "${OPCACHE_REVALIDATE_FREQ}" } - ] + ], + "resources": { + "limits": { + "memory": "${MEMORY_LIMIT}" + } + } } ] } @@ -208,70 +229,99 @@ ], "parameters": [ { + "name": "NAME", + "displayName": "Name", + "description": "The name assigned to all of the frontend objects defined in this template.", + "required": true, + "value": "cakephp-example" + }, + { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { + "name": "MEMORY_LIMIT", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the container can use.", + "value": "512Mi" + }, + { "name": "SOURCE_REPOSITORY_URL", - "description": "The URL of the repository with your application source code", + "displayName": "Git Repository URL", + "description": "The URL of the repository with your application source code.", "value": "https://github.com/openshift/cakephp-ex.git" }, { "name": "SOURCE_REPOSITORY_REF", - "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch" + "displayName": "Git Reference", + "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch." }, { "name": "CONTEXT_DIR", - "description": "Set this to the relative path to your project if it is not in the root of your repository" + "displayName": "Context Directory", + "description": "Set this to the relative path to your project if it is not in the root of your repository." }, { "name": "APPLICATION_DOMAIN", + "displayName": "Application Hostname", "description": "The exposed hostname that will route to the CakePHP service, if left blank a value will be defaulted.", "value": "" }, { "name": "GITHUB_WEBHOOK_SECRET", - "description": "A secret string used to configure the GitHub webhook", + "displayName": "GitHub Webhook Secret", + "description": "A secret string used to configure the GitHub webhook.", "generate": "expression", "from": "[a-zA-Z0-9]{40}" }, { "name": "DATABASE_SERVICE_NAME", - "description": "Database service name" + "displayName": "Database Service Name" }, { "name": "DATABASE_ENGINE", - "description": "Database engine: postgresql, mysql or sqlite (default)" + "displayName": "Database Engine", + "description": "Database engine: postgresql, mysql or sqlite (default)." }, { "name": "DATABASE_NAME", - "description": "Database name" + "displayName": "Database Name" }, { "name": "DATABASE_USER", - "description": "Database user name" + "displayName": "Database User" }, { "name": "DATABASE_PASSWORD", - "description": "Database user password" + "displayName": "Database Password" }, { "name": "CAKEPHP_SECRET_TOKEN", - "description": "Set this to a long random string", + "displayName": "CakePHP Secret Token", + "description": "Set this to a long random string.", "generate": "expression", "from": "[\\w]{50}" }, { "name": "CAKEPHP_SECURITY_SALT", - "description": "Security salt for session hash", + "displayName": "CakePHP Security Salt", + "description": "Security salt for session hash.", "generate": "expression", "from": "[a-zA-Z0-9]{40}" }, { "name": "CAKEPHP_SECURITY_CIPHER_SEED", - "description": "Security cipher seed for session hash", + "displayName": "CakePHP Security Cipher Seed", + "description": "Security cipher seed for session hash.", "generate": "expression", "from": "[0-9]{30}" }, { "name": "OPCACHE_REVALIDATE_FREQ", - "description": "The How often to check script timestamps for updates, in seconds. 0 will result in OPcache checking for updates on every request.", + "displayName": "OPcache Revalidation Frequency", + "description": "How often to check script timestamps for updates, in seconds. 0 will result in OPcache checking for updates on every request.", "value": "2" } ] diff --git a/roles/openshift_examples/files/examples/v1.1/quickstart-templates/dancer-mysql.json b/roles/openshift_examples/files/examples/v1.1/quickstart-templates/dancer-mysql.json index edc6a1f3f..c0fc02ae4 100644 --- a/roles/openshift_examples/files/examples/v1.1/quickstart-templates/dancer-mysql.json +++ b/roles/openshift_examples/files/examples/v1.1/quickstart-templates/dancer-mysql.json @@ -5,7 +5,7 @@ "name": "dancer-mysql-example", "annotations": { "description": "An example Dancer application with a MySQL database", - "tags": "instant-app,perl,dancer,mysql", + "tags": "quickstart,perl,dancer,mysql", "iconClass": "icon-perl" } }, @@ -17,7 +17,7 @@ "kind": "Service", "apiVersion": "v1", "metadata": { - "name": "dancer-mysql-example", + "name": "${NAME}", "annotations": { "description": "Exposes and load balances the application pods" } @@ -31,7 +31,7 @@ } ], "selector": { - "name": "dancer-mysql-example" + "name": "${NAME}" } } }, @@ -39,13 +39,13 @@ "kind": "Route", "apiVersion": "v1", "metadata": { - "name": "dancer-mysql-example" + "name": "${NAME}" }, "spec": { "host": "${APPLICATION_DOMAIN}", "to": { "kind": "Service", - "name": "dancer-mysql-example" + "name": "${NAME}" } } }, @@ -53,7 +53,7 @@ "kind": "ImageStream", "apiVersion": "v1", "metadata": { - "name": "dancer-mysql-example", + "name": "${NAME}", "annotations": { "description": "Keeps track of changes in the application image" } @@ -63,7 +63,7 @@ "kind": "BuildConfig", "apiVersion": "v1", "metadata": { - "name": "dancer-mysql-example", + "name": "${NAME}", "annotations": { "description": "Defines how to build the application" } @@ -82,7 +82,7 @@ "sourceStrategy": { "from": { "kind": "ImageStreamTag", - "namespace": "openshift", + "namespace": "${NAMESPACE}", "name": "perl:5.20" } } @@ -90,7 +90,7 @@ "output": { "to": { "kind": "ImageStreamTag", - "name": "dancer-mysql-example:latest" + "name": "${NAME}:latest" } }, "triggers": [ @@ -113,7 +113,7 @@ "kind": "DeploymentConfig", "apiVersion": "v1", "metadata": { - "name": "dancer-mysql-example", + "name": "${NAME}", "annotations": { "description": "Defines how to deploy the application server" } @@ -129,7 +129,7 @@ ], "from": { "kind": "ImageStreamTag", - "name": "dancer-mysql-example:latest" + "name": "${NAME}:latest" } } }, @@ -139,13 +139,13 @@ ], "replicas": 1, "selector": { - "name": "dancer-mysql-example" + "name": "${NAME}" }, "template": { "metadata": { - "name": "dancer-mysql-example", + "name": "${NAME}", "labels": { - "name": "dancer-mysql-example" + "name": "${NAME}" } }, "spec": { @@ -158,6 +158,22 @@ "containerPort": 8080 } ], + "readinessProbe": { + "timeoutSeconds": 3, + "initialDelaySeconds": 3, + "httpGet": { + "path": "/health", + "port": 8080 + } + }, + "livenessProbe": { + "timeoutSeconds": 3, + "initialDelaySeconds": 30, + "httpGet": { + "path": "/", + "port": 8080 + } + }, "env": [ { "name": "DATABASE_SERVICE_NAME", @@ -183,7 +199,12 @@ "name": "PERL_APACHE2_RELOAD", "value": "${PERL_APACHE2_RELOAD}" } - ] + ], + "resources": { + "limits": { + "memory": "${MEMORY_LIMIT}" + } + } } ] } @@ -235,7 +256,7 @@ ], "from": { "kind": "ImageStreamTag", - "namespace": "openshift", + "namespace": "${NAMESPACE}", "name": "mysql:5.6" } } @@ -265,6 +286,20 @@ "containerPort": 3306 } ], + "readinessProbe": { + "timeoutSeconds": 1, + "initialDelaySeconds": 5, + "exec": { + "command": [ "/bin/sh", "-i", "-c", "MYSQL_PWD='${DATABASE_PASSWORD}' mysql -h 127.0.0.1 -u ${DATABASE_USER} -D ${DATABASE_NAME} -e 'SELECT 1'" ] + } + }, + "livenessProbe": { + "timeoutSeconds": 1, + "initialDelaySeconds": 30, + "tcpSocket": { + "port": 3306 + } + }, "env": [ { "name": "MYSQL_USER", @@ -275,10 +310,15 @@ "value": "${DATABASE_PASSWORD}" }, { - "name": "MYSQL_DATABASE", - "value": "${DATABASE_NAME}" + "name": "MYSQL_DATABASE", + "value": "${DATABASE_NAME}" } - ] + ], + "resources": { + "limits": { + "memory": "${MEMORY_MYSQL_LIMIT}" + } + } } ] } @@ -288,71 +328,103 @@ ], "parameters": [ { + "name": "NAME", + "displayName": "Name", + "description": "The name assigned to all of the frontend objects defined in this template.", + "required": true, + "value": "dancer-mysql-example" + }, + { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { + "name": "MEMORY_LIMIT", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the Perl Dancer container can use.", + "value": "512Mi" + }, + { + "name": "MEMORY_MYSQL_LIMIT", + "displayName": "Memory Limit (MySQL)", + "description": "Maximum amount of memory the MySQL container can use.", + "value": "512Mi" + }, + { "name": "SOURCE_REPOSITORY_URL", - "description": "The URL of the repository with your application source code", + "displayName": "Git Repository URL", + "description": "The URL of the repository with your application source code.", "value": "https://github.com/openshift/dancer-ex.git" }, { "name": "SOURCE_REPOSITORY_REF", - "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch" + "displayName": "Git Reference", + "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch." }, { "name": "CONTEXT_DIR", - "description": "Set this to the relative path to your project if it is not in the root of your repository" + "displayName": "Context Directory", + "description": "Set this to the relative path to your project if it is not in the root of your repository." }, { "name": "APPLICATION_DOMAIN", + "displayName": "Application Hostname", "description": "The exposed hostname that will route to the Dancer service, if left blank a value will be defaulted.", "value": "" }, { "name": "GITHUB_WEBHOOK_SECRET", - "description": "A secret string used to configure the GitHub webhook", + "displayName": "GitHub Webhook Secret", + "description": "A secret string used to configure the GitHub webhook.", "generate": "expression", "from": "[a-zA-Z0-9]{40}" }, { "name": "ADMIN_USERNAME", - "description": "administrator username", + "displayName": "Administrator Username", "generate": "expression", "from": "admin[A-Z0-9]{3}" }, { "name": "ADMIN_PASSWORD", - "description": "administrator password", + "displayName": "Administrator Password", "generate": "expression", "from": "[a-zA-Z0-9]{8}" }, { "name": "DATABASE_SERVICE_NAME", - "description": "Database service name", + "displayName": "Database Service Name", "value": "database" }, { "name": "DATABASE_USER", - "description": "database username", + "displayName": "Database Username", "generate": "expression", "from": "user[A-Z0-9]{3}" }, { "name": "DATABASE_PASSWORD", - "description": "database password", + "displayName": "Database Password", "generate": "expression", "from": "[a-zA-Z0-9]{8}" }, { "name": "DATABASE_NAME", - "description": "database name", + "displayName": "Database Name", "value": "sampledb" }, { "name": "PERL_APACHE2_RELOAD", - "description": "Set this to \"true\" to enable automatic reloading of modified Perl modules", + "displayName": "Perl Module Reload", + "description": "Set this to \"true\" to enable automatic reloading of modified Perl modules.", "value": "" }, { "name": "SECRET_KEY_BASE", - "description": "Your secret key for verifying the integrity of signed cookies", + "displayName": "Secret Key", + "description": "Your secret key for verifying the integrity of signed cookies.", "generate": "expression", "from": "[a-z0-9]{127}" } diff --git a/roles/openshift_examples/files/examples/v1.1/quickstart-templates/dancer.json b/roles/openshift_examples/files/examples/v1.1/quickstart-templates/dancer.json index 409252d82..1ea5a21a0 100644 --- a/roles/openshift_examples/files/examples/v1.1/quickstart-templates/dancer.json +++ b/roles/openshift_examples/files/examples/v1.1/quickstart-templates/dancer.json @@ -5,7 +5,7 @@ "name": "dancer-example", "annotations": { "description": "An example Dancer application with no database", - "tags": "instant-app,perl,dancer", + "tags": "quickstart,perl,dancer", "iconClass": "icon-perl" } }, @@ -17,7 +17,7 @@ "kind": "Service", "apiVersion": "v1", "metadata": { - "name": "dancer-example", + "name": "${NAME}", "annotations": { "description": "Exposes and load balances the application pods" } @@ -31,7 +31,7 @@ } ], "selector": { - "name": "dancer-example" + "name": "${NAME}" } } }, @@ -39,13 +39,13 @@ "kind": "Route", "apiVersion": "v1", "metadata": { - "name": "dancer-example" + "name": "${NAME}" }, "spec": { "host": "${APPLICATION_DOMAIN}", "to": { "kind": "Service", - "name": "dancer-example" + "name": "${NAME}" } } }, @@ -53,7 +53,7 @@ "kind": "ImageStream", "apiVersion": "v1", "metadata": { - "name": "dancer-example", + "name": "${NAME}", "annotations": { "description": "Keeps track of changes in the application image" } @@ -63,7 +63,7 @@ "kind": "BuildConfig", "apiVersion": "v1", "metadata": { - "name": "dancer-example", + "name": "${NAME}", "annotations": { "description": "Defines how to build the application" } @@ -82,7 +82,7 @@ "sourceStrategy": { "from": { "kind": "ImageStreamTag", - "namespace": "openshift", + "namespace": "${NAMESPACE}", "name": "perl:5.20" } } @@ -90,7 +90,7 @@ "output": { "to": { "kind": "ImageStreamTag", - "name": "dancer-example:latest" + "name": "${NAME}:latest" } }, "triggers": [ @@ -113,7 +113,7 @@ "kind": "DeploymentConfig", "apiVersion": "v1", "metadata": { - "name": "dancer-example", + "name": "${NAME}", "annotations": { "description": "Defines how to deploy the application server" } @@ -132,7 +132,7 @@ ], "from": { "kind": "ImageStreamTag", - "name": "dancer-example:latest" + "name": "${NAME}:latest" } } }, @@ -142,13 +142,13 @@ ], "replicas": 1, "selector": { - "name": "dancer-example" + "name": "${NAME}" }, "template": { "metadata": { - "name": "dancer-example", + "name": "${NAME}", "labels": { - "name": "dancer-example" + "name": "${NAME}" } }, "spec": { @@ -161,12 +161,33 @@ "containerPort": 8080 } ], + "readinessProbe": { + "timeoutSeconds": 3, + "initialDelaySeconds": 3, + "httpGet": { + "path": "/", + "port": 8080 + } + }, + "livenessProbe": { + "timeoutSeconds": 3, + "initialDelaySeconds": 30, + "httpGet": { + "path": "/", + "port": 8080 + } + }, "env": [ { "name": "PERL_APACHE2_RELOAD", "value": "${PERL_APACHE2_RELOAD}" } - ] + ], + "resources": { + "limits": { + "memory": "${MEMORY_LIMIT}" + } + } } ] } @@ -176,38 +197,64 @@ ], "parameters": [ { + "name": "NAME", + "displayName": "Name", + "description": "The name assigned to all of the frontend objects defined in this template.", + "required": true, + "value": "dancer-example" + }, + { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { + "name": "MEMORY_LIMIT", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the container can use.", + "value": "512Mi" + }, + { "name": "SOURCE_REPOSITORY_URL", - "description": "The URL of the repository with your application source code", + "displayName": "Git Repository URL", + "description": "The URL of the repository with your application source code.", "value": "https://github.com/openshift/dancer-ex.git" }, { "name": "SOURCE_REPOSITORY_REF", - "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch" + "displayName": "Git Reference", + "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch." }, { "name": "CONTEXT_DIR", - "description": "Set this to the relative path to your project if it is not in the root of your repository" + "displayName": "Context Directory", + "description": "Set this to the relative path to your project if it is not in the root of your repository." }, { "name": "APPLICATION_DOMAIN", + "displayName": "Application Hostname", "description": "The exposed hostname that will route to the Dancer service, if left blank a value will be defaulted.", "value": "" }, { "name": "GITHUB_WEBHOOK_SECRET", - "description": "A secret string used to configure the GitHub webhook", + "displayName": "GitHub Webhook Secret", + "description": "A secret string used to configure the GitHub webhook.", "generate": "expression", "from": "[a-zA-Z0-9]{40}" }, { "name": "SECRET_KEY_BASE", - "description": "Your secret key for verifying the integrity of signed cookies", + "displayName": "Secret Key", + "description": "Your secret key for verifying the integrity of signed cookies.", "generate": "expression", "from": "[a-z0-9]{127}" }, { "name": "PERL_APACHE2_RELOAD", - "description": "Set this to \"true\" to enable automatic reloading of modified Perl modules", + "displayName": "Perl Module Reload", + "description": "Set this to \"true\" to enable automatic reloading of modified Perl modules.", "value": "" } ] diff --git a/roles/openshift_examples/files/examples/v1.1/quickstart-templates/django-postgresql.json b/roles/openshift_examples/files/examples/v1.1/quickstart-templates/django-postgresql.json index c4c55ddd8..844201e7c 100644 --- a/roles/openshift_examples/files/examples/v1.1/quickstart-templates/django-postgresql.json +++ b/roles/openshift_examples/files/examples/v1.1/quickstart-templates/django-postgresql.json @@ -5,7 +5,7 @@ "name": "django-psql-example", "annotations": { "description": "An example Django application with a PostgreSQL database", - "tags": "instant-app,python,django,postgresql", + "tags": "quickstart,python,django,postgresql", "iconClass": "icon-python" } }, @@ -17,7 +17,7 @@ "kind": "Service", "apiVersion": "v1", "metadata": { - "name": "django-psql-example", + "name": "${NAME}", "annotations": { "description": "Exposes and load balances the application pods" } @@ -31,7 +31,7 @@ } ], "selector": { - "name": "django-psql-example" + "name": "${NAME}" } } }, @@ -39,13 +39,13 @@ "kind": "Route", "apiVersion": "v1", "metadata": { - "name": "django-psql-example" + "name": "${NAME}" }, "spec": { "host": "${APPLICATION_DOMAIN}", "to": { "kind": "Service", - "name": "django-psql-example" + "name": "${NAME}" } } }, @@ -53,7 +53,7 @@ "kind": "ImageStream", "apiVersion": "v1", "metadata": { - "name": "django-psql-example", + "name": "${NAME}", "annotations": { "description": "Keeps track of changes in the application image" } @@ -63,7 +63,7 @@ "kind": "BuildConfig", "apiVersion": "v1", "metadata": { - "name": "django-psql-example", + "name": "${NAME}", "annotations": { "description": "Defines how to build the application" } @@ -82,7 +82,7 @@ "sourceStrategy": { "from": { "kind": "ImageStreamTag", - "namespace": "openshift", + "namespace": "${NAMESPACE}", "name": "python:3.4" } } @@ -90,7 +90,7 @@ "output": { "to": { "kind": "ImageStreamTag", - "name": "django-psql-example:latest" + "name": "${NAME}:latest" } }, "triggers": [ @@ -106,14 +106,17 @@ "secret": "${GITHUB_WEBHOOK_SECRET}" } } - ] + ], + "postCommit": { + "script": "./manage.py test" + } } }, { "kind": "DeploymentConfig", "apiVersion": "v1", "metadata": { - "name": "django-psql-example", + "name": "${NAME}", "annotations": { "description": "Defines how to deploy the application server" } @@ -132,7 +135,7 @@ ], "from": { "kind": "ImageStreamTag", - "name": "django-psql-example:latest" + "name": "${NAME}:latest" } } }, @@ -142,13 +145,13 @@ ], "replicas": 1, "selector": { - "name": "django-psql-example" + "name": "${NAME}" }, "template": { "metadata": { - "name": "django-psql-example", + "name": "${NAME}", "labels": { - "name": "django-psql-example" + "name": "${NAME}" } }, "spec": { @@ -161,6 +164,22 @@ "containerPort": 8080 } ], + "readinessProbe": { + "timeoutSeconds": 3, + "initialDelaySeconds": 3, + "httpGet": { + "path": "/health", + "port": 8080 + } + }, + "livenessProbe": { + "timeoutSeconds": 3, + "initialDelaySeconds": 30, + "httpGet": { + "path": "/health", + "port": 8080 + } + }, "env": [ { "name": "DATABASE_SERVICE_NAME", @@ -190,7 +209,12 @@ "name": "DJANGO_SECRET_KEY", "value": "${DJANGO_SECRET_KEY}" } - ] + ], + "resources": { + "limits": { + "memory": "${MEMORY_LIMIT}" + } + } } ] } @@ -242,7 +266,7 @@ ], "from": { "kind": "ImageStreamTag", - "namespace": "openshift", + "namespace": "${NAMESPACE}", "name": "postgresql:9.4" } } @@ -285,7 +309,26 @@ "name": "POSTGRESQL_DATABASE", "value": "${DATABASE_NAME}" } - ] + ], + "readinessProbe": { + "timeoutSeconds": 1, + "initialDelaySeconds": 5, + "exec": { + "command": [ "/bin/sh", "-i", "-c", "psql -h 127.0.0.1 -U ${POSTGRESQL_USER} -q -d ${POSTGRESQL_DATABASE} -c 'SELECT 1'"] + } + }, + "livenessProbe": { + "timeoutSeconds": 1, + "initialDelaySeconds": 30, + "tcpSocket": { + "port": 5432 + } + }, + "resources": { + "limits": { + "memory": "${MEMORY_POSTGRESQL_LIMIT}" + } + } } ] } @@ -295,62 +338,95 @@ ], "parameters": [ { + "name": "NAME", + "displayName": "Name", + "description": "The name assigned to all of the frontend objects defined in this template.", + "required": true, + "value": "django-psql-example" + }, + { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { + "name": "MEMORY_LIMIT", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the Django container can use.", + "value": "512Mi" + }, + { + "name": "MEMORY_POSTGRESQL_LIMIT", + "displayName": "Memory Limit (PostgreSQL)", + "description": "Maximum amount of memory the PostgreSQL container can use.", + "value": "512Mi" + }, + { "name": "SOURCE_REPOSITORY_URL", - "description": "The URL of the repository with your application source code", + "displayName": "Git Repository URL", + "description": "The URL of the repository with your application source code.", "value": "https://github.com/openshift/django-ex.git" }, { "name": "SOURCE_REPOSITORY_REF", - "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch" + "displayName": "Git Reference", + "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch." }, { "name": "CONTEXT_DIR", - "description": "Set this to the relative path to your project if it is not in the root of your repository" + "displayName": "Context Directory", + "description": "Set this to the relative path to your project if it is not in the root of your repository." }, { "name": "APPLICATION_DOMAIN", + "displayName": "Application Hostname", "description": "The exposed hostname that will route to the Django service, if left blank a value will be defaulted.", "value": "" }, { "name": "GITHUB_WEBHOOK_SECRET", - "description": "A secret string used to configure the GitHub webhook", + "displayName": "GitHub Webhook Secret", + "description": "A secret string used to configure the GitHub webhook.", "generate": "expression", "from": "[a-zA-Z0-9]{40}" }, { "name": "DATABASE_SERVICE_NAME", - "description": "Database service name", + "displayName": "Database Service Name", "value": "postgresql" }, { "name": "DATABASE_ENGINE", - "description": "Database engine: postgresql, mysql or sqlite (default)", + "displayName": "Database Engine", + "description": "Database engine: postgresql, mysql or sqlite (default).", "value": "postgresql" }, { "name": "DATABASE_NAME", - "description": "Database name", + "displayName": "Database Name", "value": "default" }, { "name": "DATABASE_USER", - "description": "Database user name", + "displayName": "Database Username", "value": "django" }, { "name": "DATABASE_PASSWORD", - "description": "Database user password", + "displayName": "Database User Password", "generate": "expression", "from": "[a-zA-Z0-9]{16}" }, { "name": "APP_CONFIG", - "description": "Relative path to Gunicorn configuration file (optional)" + "displayName": "Application Configuration File Path", + "description": "Relative path to Gunicorn configuration file (optional)." }, { "name": "DJANGO_SECRET_KEY", - "description": "Set this to a long random string", + "displayName": "Djange Secret Key", + "description": "Set this to a long random string.", "generate": "expression", "from": "[\\w]{50}" } diff --git a/roles/openshift_examples/files/examples/v1.1/quickstart-templates/django.json b/roles/openshift_examples/files/examples/v1.1/quickstart-templates/django.json index 75b6798b5..38ef694f8 100644 --- a/roles/openshift_examples/files/examples/v1.1/quickstart-templates/django.json +++ b/roles/openshift_examples/files/examples/v1.1/quickstart-templates/django.json @@ -5,7 +5,7 @@ "name": "django-example", "annotations": { "description": "An example Django application with no database", - "tags": "instant-app,python,django", + "tags": "quickstart,python,django", "iconClass": "icon-python" } }, @@ -17,7 +17,7 @@ "kind": "Service", "apiVersion": "v1", "metadata": { - "name": "django-example", + "name": "${NAME}", "annotations": { "description": "Exposes and load balances the application pods" } @@ -31,7 +31,7 @@ } ], "selector": { - "name": "django-example" + "name": "${NAME}" } } }, @@ -39,13 +39,13 @@ "kind": "Route", "apiVersion": "v1", "metadata": { - "name": "django-example" + "name": "${NAME}" }, "spec": { "host": "${APPLICATION_DOMAIN}", "to": { "kind": "Service", - "name": "django-example" + "name": "${NAME}" } } }, @@ -53,7 +53,7 @@ "kind": "ImageStream", "apiVersion": "v1", "metadata": { - "name": "django-example", + "name": "${NAME}", "annotations": { "description": "Keeps track of changes in the application image" } @@ -63,7 +63,7 @@ "kind": "BuildConfig", "apiVersion": "v1", "metadata": { - "name": "django-example", + "name": "${NAME}", "annotations": { "description": "Defines how to build the application" } @@ -82,7 +82,7 @@ "sourceStrategy": { "from": { "kind": "ImageStreamTag", - "namespace": "openshift", + "namespace": "${NAMESPACE}", "name": "python:3.4" } } @@ -90,7 +90,7 @@ "output": { "to": { "kind": "ImageStreamTag", - "name": "django-example:latest" + "name": "${NAME}:latest" } }, "triggers": [ @@ -106,14 +106,17 @@ "secret": "${GITHUB_WEBHOOK_SECRET}" } } - ] + ], + "postCommit": { + "script": "./manage.py test" + } } }, { "kind": "DeploymentConfig", "apiVersion": "v1", "metadata": { - "name": "django-example", + "name": "${NAME}", "annotations": { "description": "Defines how to deploy the application server" } @@ -132,7 +135,7 @@ ], "from": { "kind": "ImageStreamTag", - "name": "django-example:latest" + "name": "${NAME}:latest" } } }, @@ -142,13 +145,13 @@ ], "replicas": 1, "selector": { - "name": "django-example" + "name": "${NAME}" }, "template": { "metadata": { - "name": "django-example", + "name": "${NAME}", "labels": { - "name": "django-example" + "name": "${NAME}" } }, "spec": { @@ -161,6 +164,22 @@ "containerPort": 8080 } ], + "readinessProbe": { + "timeoutSeconds": 3, + "initialDelaySeconds": 3, + "httpGet": { + "path": "/", + "port": 8080 + } + }, + "livenessProbe": { + "timeoutSeconds": 3, + "initialDelaySeconds": 30, + "httpGet": { + "path": "/", + "port": 8080 + } + }, "env": [ { "name": "DATABASE_SERVICE_NAME", @@ -190,7 +209,12 @@ "name": "DJANGO_SECRET_KEY", "value": "${DJANGO_SECRET_KEY}" } - ] + ], + "resources": { + "limits": { + "memory": "${MEMORY_LIMIT}" + } + } } ] } @@ -200,56 +224,83 @@ ], "parameters": [ { + "name": "NAME", + "displayName": "Name", + "description": "The name assigned to all of the frontend objects defined in this template.", + "required": true, + "value": "django-example" + }, + { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { + "name": "MEMORY_LIMIT", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the container can use.", + "value": "512Mi" + }, + { "name": "SOURCE_REPOSITORY_URL", - "description": "The URL of the repository with your application source code", + "displayName": "Git Repository URL", + "description": "The URL of the repository with your application source code.", "value": "https://github.com/openshift/django-ex.git" }, { "name": "SOURCE_REPOSITORY_REF", - "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch" + "displayName": "Git Reference", + "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch." }, { "name": "CONTEXT_DIR", - "description": "Set this to the relative path to your project if it is not in the root of your repository" + "displayName": "Context Directory", + "description": "Set this to the relative path to your project if it is not in the root of your repository." }, { "name": "APPLICATION_DOMAIN", + "displayName": "Application Hostname", "description": "The exposed hostname that will route to the Django service, if left blank a value will be defaulted.", "value": "" }, { "name": "GITHUB_WEBHOOK_SECRET", - "description": "A secret string used to configure the GitHub webhook", + "displayName": "GitHub Webhook Secret", + "description": "A secret string used to configure the GitHub webhook.", "generate": "expression", "from": "[a-zA-Z0-9]{40}" }, { "name": "DATABASE_SERVICE_NAME", - "description": "Database service name" + "displayName": "Database Service Name" }, { "name": "DATABASE_ENGINE", - "description": "Database engine: postgresql, mysql or sqlite (default)" + "displayName": "Database Engine", + "description": "Database engine: postgresql, mysql or sqlite (default)." }, { "name": "DATABASE_NAME", - "description": "Database name" + "displayName": "Database Name" }, { "name": "DATABASE_USER", - "description": "Database user name" + "displayName": "Database Username" }, { "name": "DATABASE_PASSWORD", - "description": "Database user password" + "displayName": "Database User Password" }, { "name": "APP_CONFIG", - "description": "Relative path to Gunicorn configuration file (optional)" + "displayName": "Application Configuration File Path", + "description": "Relative path to Gunicorn configuration file (optional)." }, { "name": "DJANGO_SECRET_KEY", - "description": "Set this to a long random string", + "displayName": "Django Secret Key", + "description": "Set this to a long random string.", "generate": "expression", "from": "[\\w]{50}" } diff --git a/roles/openshift_examples/files/examples/v1.1/quickstart-templates/jenkins-ephemeral-template.json b/roles/openshift_examples/files/examples/v1.1/quickstart-templates/jenkins-ephemeral-template.json index 0b016373f..e464b5971 100644 --- a/roles/openshift_examples/files/examples/v1.1/quickstart-templates/jenkins-ephemeral-template.json +++ b/roles/openshift_examples/files/examples/v1.1/quickstart-templates/jenkins-ephemeral-template.json @@ -65,8 +65,7 @@ }, "spec": { "strategy": { - "type": "Recreate", - "resources": {} + "type": "Recreate" }, "triggers": [ { @@ -79,7 +78,7 @@ "from": { "kind": "ImageStreamTag", "name": "jenkins:latest", - "namespace": "openshift" + "namespace": "${NAMESPACE}" }, "lastTriggeredImage": "" } @@ -104,13 +103,33 @@ { "name": "jenkins", "image": "${JENKINS_IMAGE}", + "readinessProbe": { + "timeoutSeconds": 3, + "initialDelaySeconds": 3, + "httpGet": { + "path": "/login", + "port": 8080 + } + }, + "livenessProbe": { + "timeoutSeconds": 3, + "initialDelaySeconds": 30, + "httpGet": { + "path": "/login", + "port": 8080 + } + }, "env": [ { "name": "JENKINS_PASSWORD", "value": "${JENKINS_PASSWORD}" } ], - "resources": {}, + "resources": { + "limits": { + "memory": "${MEMORY_LIMIT}" + } + }, "volumeMounts": [ { "name": "${JENKINS_SERVICE_NAME}-data", @@ -143,13 +162,27 @@ ], "parameters": [ { + "name": "MEMORY_LIMIT", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the container can use.", + "value": "512Mi" + }, + { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { "name": "JENKINS_SERVICE_NAME", - "description": "Jenkins service name", + "displayName": "Jenkins Service Name", + "description": "The name of the OpenShift Service exposed for the Jenkins container.", "value": "jenkins" }, { "name": "JENKINS_PASSWORD", - "description": "Password for the Jenkins user", + "displayName": "Jenkins Password", + "description": "Password for the Jenkins user.", "generate": "expression", "value": "password" } diff --git a/roles/openshift_examples/files/examples/v1.1/quickstart-templates/jenkins-persistent-template.json b/roles/openshift_examples/files/examples/v1.1/quickstart-templates/jenkins-persistent-template.json index 98f0cea95..6c143fc70 100644 --- a/roles/openshift_examples/files/examples/v1.1/quickstart-templates/jenkins-persistent-template.json +++ b/roles/openshift_examples/files/examples/v1.1/quickstart-templates/jenkins-persistent-template.json @@ -82,8 +82,7 @@ }, "spec": { "strategy": { - "type": "Recreate", - "resources": {} + "type": "Recreate" }, "triggers": [ { @@ -96,7 +95,7 @@ "from": { "kind": "ImageStreamTag", "name": "jenkins:latest", - "namespace": "openshift" + "namespace": "${NAMESPACE}" }, "lastTriggeredImage": "" } @@ -121,13 +120,33 @@ { "name": "jenkins", "image": "${JENKINS_IMAGE}", + "readinessProbe": { + "timeoutSeconds": 3, + "initialDelaySeconds": 3, + "httpGet": { + "path": "/login", + "port": 8080 + } + }, + "livenessProbe": { + "timeoutSeconds": 3, + "initialDelaySeconds": 30, + "httpGet": { + "path": "/login", + "port": 8080 + } + }, "env": [ { "name": "JENKINS_PASSWORD", "value": "${JENKINS_PASSWORD}" } ], - "resources": {}, + "resources": { + "limits": { + "memory": "${MEMORY_LIMIT}" + } + }, "volumeMounts": [ { "name": "${JENKINS_SERVICE_NAME}-data", @@ -160,19 +179,34 @@ ], "parameters": [ { + "name": "MEMORY_LIMIT", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the container can use.", + "value": "512Mi" + }, + { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { "name": "JENKINS_SERVICE_NAME", - "description": "Jenkins service name", + "displayName": "Jenkins Service Name", + "description": "The name of the OpenShift Service exposed for the Jenkins container.", "value": "jenkins" }, { "name": "JENKINS_PASSWORD", - "description": "Password for the Jenkins user", + "displayName": "Jenkins Password", + "description": "Password for the Jenkins user.", "generate": "expression", "value": "password" }, { "name": "VOLUME_CAPACITY", - "description": "Volume space available for data, e.g. 512Mi, 2Gi", + "displayName": "Volume Capacity", + "description": "Volume space available for data, e.g. 512Mi, 2Gi.", "value": "512Mi", "required": true } diff --git a/roles/openshift_examples/files/examples/v1.1/quickstart-templates/nodejs-mongodb.json b/roles/openshift_examples/files/examples/v1.1/quickstart-templates/nodejs-mongodb.json index 21f943da7..3298ef40c 100644 --- a/roles/openshift_examples/files/examples/v1.1/quickstart-templates/nodejs-mongodb.json +++ b/roles/openshift_examples/files/examples/v1.1/quickstart-templates/nodejs-mongodb.json @@ -5,7 +5,7 @@ "name": "nodejs-mongodb-example", "annotations": { "description": "An example Node.js application with a MongoDB database", - "tags": "instant-app,nodejs,mongodb", + "tags": "quickstart,nodejs,mongodb", "iconClass": "icon-nodejs" } }, @@ -17,7 +17,7 @@ "kind": "Service", "apiVersion": "v1", "metadata": { - "name": "nodejs-mongodb-example", + "name": "${NAME}", "annotations": { "description": "Exposes and load balances the application pods" } @@ -31,7 +31,7 @@ } ], "selector": { - "name": "nodejs-mongodb-example" + "name": "${NAME}" } } }, @@ -39,13 +39,13 @@ "kind": "Route", "apiVersion": "v1", "metadata": { - "name": "nodejs-mongodb-example" + "name": "${NAME}" }, "spec": { "host": "${APPLICATION_DOMAIN}", "to": { "kind": "Service", - "name": "nodejs-mongodb-example" + "name": "${NAME}" } } }, @@ -53,7 +53,7 @@ "kind": "ImageStream", "apiVersion": "v1", "metadata": { - "name": "nodejs-mongodb-example", + "name": "${NAME}", "annotations": { "description": "Keeps track of changes in the application image" } @@ -63,7 +63,7 @@ "kind": "BuildConfig", "apiVersion": "v1", "metadata": { - "name": "nodejs-mongodb-example", + "name": "${NAME}", "annotations": { "description": "Defines how to build the application" } @@ -82,7 +82,7 @@ "sourceStrategy": { "from": { "kind": "ImageStreamTag", - "namespace": "openshift", + "namespace": "${NAMESPACE}", "name": "nodejs:0.10" } } @@ -90,7 +90,7 @@ "output": { "to": { "kind": "ImageStreamTag", - "name": "nodejs-mongodb-example:latest" + "name": "${NAME}:latest" } }, "triggers": [ @@ -119,7 +119,7 @@ "kind": "DeploymentConfig", "apiVersion": "v1", "metadata": { - "name": "nodejs-mongodb-example", + "name": "${NAME}", "annotations": { "description": "Defines how to deploy the application server" } @@ -138,7 +138,7 @@ ], "from": { "kind": "ImageStreamTag", - "name": "nodejs-mongodb-example:latest" + "name": "${NAME}:latest" } } }, @@ -148,13 +148,13 @@ ], "replicas": 1, "selector": { - "name": "nodejs-mongodb-example" + "name": "${NAME}" }, "template": { "metadata": { - "name": "nodejs-mongodb-example", + "name": "${NAME}", "labels": { - "name": "nodejs-mongodb-example" + "name": "${NAME}" } }, "spec": { @@ -188,7 +188,28 @@ "name": "MONGODB_ADMIN_PASSWORD", "value": "${DATABASE_ADMIN_PASSWORD}" } - ] + ], + "readinessProbe": { + "timeoutSeconds": 3, + "initialDelaySeconds": 3, + "httpGet": { + "path": "/pagecount", + "port": 8080 + } + }, + "livenessProbe": { + "timeoutSeconds": 3, + "initialDelaySeconds": 30, + "httpGet": { + "path": "/pagecount", + "port": 8080 + } + }, + "resources": { + "limits": { + "memory": "${MEMORY_LIMIT}" + } + } } ] } @@ -240,7 +261,7 @@ ], "from": { "kind": "ImageStreamTag", - "namespace": "openshift", + "namespace": "${NAMESPACE}", "name": "mongodb:2.6" } } @@ -287,7 +308,26 @@ "name": "MONGODB_ADMIN_PASSWORD", "value": "${DATABASE_ADMIN_PASSWORD}" } - ] + ], + "readinessProbe": { + "timeoutSeconds": 1, + "initialDelaySeconds": 3, + "exec": { + "command": [ "/bin/sh", "-i", "-c", "mongostat --host 127.0.0.1 -u admin -p ${DATABASE_ADMIN_PASSWORD} -n 1 --noheaders"] + } + }, + "livenessProbe": { + "timeoutSeconds": 1, + "initialDelaySeconds": 30, + "tcpSocket": { + "port": 27017 + } + }, + "resources": { + "limits": { + "memory": "${MEMORY_MONGODB_LIMIT}" + } + } } ] } @@ -297,60 +337,94 @@ ], "parameters": [ { + "name": "NAME", + "displayName": "Name", + "description": "The name assigned to all of the frontend objects defined in this template.", + "required": true, + "value": "nodejs-mongodb-example" + }, + { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { + "name": "MEMORY_LIMIT", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the Node.js container can use.", + "value": "512Mi" + }, + { + "name": "MEMORY_MONGODB_LIMIT", + "displayName": "Memory Limit (MongoDB)", + "description": "Maximum amount of memory the MongoDB container can use.", + "value": "512Mi" + }, + { "name": "SOURCE_REPOSITORY_URL", - "description": "The URL of the repository with your application source code", + "displayName": "Git Repository URL", + "description": "The URL of the repository with your application source code.", "value": "https://github.com/openshift/nodejs-ex.git" }, { "name": "SOURCE_REPOSITORY_REF", - "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch" + "displayName": "Git Reference", + "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch." }, { "name": "CONTEXT_DIR", - "description": "Set this to the relative path to your project if it is not in the root of your repository" + "displayName": "Context Directory", + "description": "Set this to the relative path to your project if it is not in the root of your repository." }, { "name": "APPLICATION_DOMAIN", + "displayName": "Application Hostname", "description": "The exposed hostname that will route to the Node.js service, if left blank a value will be defaulted.", "value": "" }, { "name": "GITHUB_WEBHOOK_SECRET", - "description": "A secret string used to configure the GitHub webhook", + "displayName": "GitHub Webhook Secret", + "description": "A secret string used to configure the GitHub webhook.", "generate": "expression", "from": "[a-zA-Z0-9]{40}" }, { "name": "GENERIC_WEBHOOK_SECRET", - "description": "A secret string used to configure the Generic webhook", + "displayName": "Generic Webhook Secret", + "description": "A secret string used to configure the Generic webhook.", "generate": "expression", "from": "[a-zA-Z0-9]{40}" }, { "name": "DATABASE_SERVICE_NAME", - "description": "Database service name", + "displayName": "Database Service Name", "value": "mongodb" }, { "name": "DATABASE_USER", - "description": "Username for MongoDB user that will be used for accessing the database", + "displayName": "MongoDB Username", + "description": "Username for MongoDB user that will be used for accessing the database.", "generate": "expression", "from": "user[A-Z0-9]{3}" }, { "name": "DATABASE_PASSWORD", - "description": "Password for the MongoDB user", + "displayName": "MongoDB Password", + "description": "Password for the MongoDB user.", "generate": "expression", "from": "[a-zA-Z0-9]{16}" }, { "name": "DATABASE_NAME", - "description": "Database name", + "displayName": "Database Name", "value": "sampledb" }, { "name": "DATABASE_ADMIN_PASSWORD", - "description": "Password for the database admin user", + "displayName": "Database Administrator Password", + "description": "Password for the database admin user.", "generate": "expression", "from": "[a-zA-Z0-9]{16}" } diff --git a/roles/openshift_examples/files/examples/v1.1/quickstart-templates/nodejs.json b/roles/openshift_examples/files/examples/v1.1/quickstart-templates/nodejs.json index 1e301c076..82df67c4e 100644 --- a/roles/openshift_examples/files/examples/v1.1/quickstart-templates/nodejs.json +++ b/roles/openshift_examples/files/examples/v1.1/quickstart-templates/nodejs.json @@ -5,7 +5,7 @@ "name": "nodejs-example", "annotations": { "description": "An example Node.js application with no database", - "tags": "instant-app,nodejs", + "tags": "quickstart,nodejs", "iconClass": "icon-nodejs" } }, @@ -17,7 +17,7 @@ "kind": "Service", "apiVersion": "v1", "metadata": { - "name": "nodejs-example", + "name": "${NAME}", "annotations": { "description": "Exposes and load balances the application pods" } @@ -31,7 +31,7 @@ } ], "selector": { - "name": "nodejs-example" + "name": "${NAME}" } } }, @@ -39,13 +39,13 @@ "kind": "Route", "apiVersion": "v1", "metadata": { - "name": "nodejs-example" + "name": "${NAME}" }, "spec": { "host": "${APPLICATION_DOMAIN}", "to": { "kind": "Service", - "name": "nodejs-example" + "name": "${NAME}" } } }, @@ -53,7 +53,7 @@ "kind": "ImageStream", "apiVersion": "v1", "metadata": { - "name": "nodejs-example", + "name": "${NAME}", "annotations": { "description": "Keeps track of changes in the application image" } @@ -63,7 +63,7 @@ "kind": "BuildConfig", "apiVersion": "v1", "metadata": { - "name": "nodejs-example", + "name": "${NAME}", "annotations": { "description": "Defines how to build the application" } @@ -82,7 +82,7 @@ "sourceStrategy": { "from": { "kind": "ImageStreamTag", - "namespace": "openshift", + "namespace": "${NAMESPACE}", "name": "nodejs:0.10" } } @@ -90,7 +90,7 @@ "output": { "to": { "kind": "ImageStreamTag", - "name": "nodejs-example:latest" + "name": "${NAME}:latest" } }, "triggers": [ @@ -119,7 +119,7 @@ "kind": "DeploymentConfig", "apiVersion": "v1", "metadata": { - "name": "nodejs-example", + "name": "${NAME}", "annotations": { "description": "Defines how to deploy the application server" } @@ -138,7 +138,7 @@ ], "from": { "kind": "ImageStreamTag", - "name": "nodejs-example:latest" + "name": "${NAME}:latest" } } }, @@ -148,13 +148,13 @@ ], "replicas": 1, "selector": { - "name": "nodejs-example" + "name": "${NAME}" }, "template": { "metadata": { - "name": "nodejs-example", + "name": "${NAME}", "labels": { - "name": "nodejs-example" + "name": "${NAME}" } }, "spec": { @@ -167,6 +167,27 @@ "containerPort": 8080 } ], + "readinessProbe": { + "timeoutSeconds": 3, + "initialDelaySeconds": 3, + "httpGet": { + "path": "/", + "port": 8080 + } + }, + "livenessProbe": { + "timeoutSeconds": 3, + "initialDelaySeconds": 30, + "httpGet": { + "path": "/", + "port": 8080 + } + }, + "resources": { + "limits": { + "memory": "${MEMORY_LIMIT}" + } + }, "env": [ { "name": "DATABASE_SERVICE_NAME", @@ -188,7 +209,12 @@ "name": "MONGODB_ADMIN_PASSWORD", "value": "${MONGODB_ADMIN_PASSWORD}" } - ] + ], + "resources": { + "limits": { + "memory": "${MEMORY_LIMIT}" + } + } } ] } @@ -198,54 +224,82 @@ ], "parameters": [ { + "name": "NAME", + "displayName": "Name", + "description": "The name assigned to all of the frontend objects defined in this template.", + "required": true, + "value": "nodejs-example" + }, + { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { + "name": "MEMORY_LIMIT", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the container can use.", + "value": "512Mi" + }, + { "name": "SOURCE_REPOSITORY_URL", - "description": "The URL of the repository with your application source code", + "displayName": "Git Repository URL", + "description": "The URL of the repository with your application source code.", "value": "https://github.com/openshift/nodejs-ex.git" }, { "name": "SOURCE_REPOSITORY_REF", - "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch" + "displayName": "Git Reference", + "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch." }, { "name": "CONTEXT_DIR", - "description": "Set this to the relative path to your project if it is not in the root of your repository" + "displayName": "Context Directory", + "description": "Set this to the relative path to your project if it is not in the root of your repository." }, { "name": "APPLICATION_DOMAIN", + "displayName": "Application Hostname", "description": "The exposed hostname that will route to the Node.js service, if left blank a value will be defaulted.", "value": "" }, { "name": "GITHUB_WEBHOOK_SECRET", - "description": "A secret string used to configure the GitHub webhook", + "displayName": "GitHub Webhook Secret", + "description": "A secret string used to configure the GitHub webhook.", "generate": "expression", "from": "[a-zA-Z0-9]{40}" }, { "name": "GENERIC_WEBHOOK_SECRET", - "description": "A secret string used to configure the Generic webhook", + "displayName": "Generic Webhook Secret", + "description": "A secret string used to configure the Generic webhook.", "generate": "expression", "from": "[a-zA-Z0-9]{40}" }, { "name": "DATABASE_SERVICE_NAME", - "description": "Database service name" + "displayName": "Database Service Name" }, { "name": "MONGODB_USER", - "description": "Username for MongoDB user that will be used for accessing the database" + "displayName": "MongoDB Username", + "description": "Username for MongoDB user that will be used for accessing the database." }, { "name": "MONGODB_PASSWORD", - "description": "Password for the MongoDB user" + "displayName": "MongoDB Password", + "description": "Password for the MongoDB user." }, { "name": "MONGODB_DATABASE", - "description": "Database name" + "displayName": "Database Name" }, { "name": "MONGODB_ADMIN_PASSWORD", - "description": "Password for the database admin user" + "displayName": "Database Administrator Password", + "description": "Password for the database admin user." } ] } diff --git a/roles/openshift_examples/files/examples/v1.1/quickstart-templates/rails-postgresql.json b/roles/openshift_examples/files/examples/v1.1/quickstart-templates/rails-postgresql.json index 5dcbbc729..6292cf3e7 100644 --- a/roles/openshift_examples/files/examples/v1.1/quickstart-templates/rails-postgresql.json +++ b/roles/openshift_examples/files/examples/v1.1/quickstart-templates/rails-postgresql.json @@ -5,7 +5,7 @@ "name": "rails-postgresql-example", "annotations": { "description": "An example Rails application with a PostgreSQL database", - "tags": "instant-app,ruby,rails,postgresql", + "tags": "quickstart,ruby,rails,postgresql", "iconClass": "icon-ruby" } }, @@ -17,7 +17,7 @@ "kind": "Service", "apiVersion": "v1", "metadata": { - "name": "rails-postgresql-example", + "name": "${NAME}", "annotations": { "description": "Exposes and load balances the application pods" } @@ -31,7 +31,7 @@ } ], "selector": { - "name": "rails-postgresql-example" + "name": "${NAME}" } } }, @@ -39,13 +39,13 @@ "kind": "Route", "apiVersion": "v1", "metadata": { - "name": "rails-postgresql-example" + "name": "${NAME}" }, "spec": { "host": "${APPLICATION_DOMAIN}", "to": { "kind": "Service", - "name": "rails-postgresql-example" + "name": "${NAME}" } } }, @@ -53,7 +53,7 @@ "kind": "ImageStream", "apiVersion": "v1", "metadata": { - "name": "rails-postgresql-example", + "name": "${NAME}", "annotations": { "description": "Keeps track of changes in the application image" } @@ -63,7 +63,7 @@ "kind": "BuildConfig", "apiVersion": "v1", "metadata": { - "name": "rails-postgresql-example", + "name": "${NAME}", "annotations": { "description": "Defines how to build the application" } @@ -82,7 +82,7 @@ "sourceStrategy": { "from": { "kind": "ImageStreamTag", - "namespace": "openshift", + "namespace": "${NAMESPACE}", "name": "ruby:2.2" } } @@ -90,7 +90,7 @@ "output": { "to": { "kind": "ImageStreamTag", - "name": "rails-postgresql-example:latest" + "name": "${NAME}:latest" } }, "triggers": [ @@ -106,14 +106,17 @@ "secret": "${GITHUB_WEBHOOK_SECRET}" } } - ] + ], + "postCommit": { + "script": "bundle exec rake test" + } } }, { "kind": "DeploymentConfig", "apiVersion": "v1", "metadata": { - "name": "rails-postgresql-example", + "name": "${NAME}", "annotations": { "description": "Defines how to deploy the application server" } @@ -128,7 +131,7 @@ "command": [ "./migrate-database.sh" ], - "containerName": "rails-postgresql-example" + "containerName": "${NAME}" } } } @@ -143,7 +146,7 @@ ], "from": { "kind": "ImageStreamTag", - "name": "rails-postgresql-example:latest" + "name": "${NAME}:latest" } } }, @@ -153,13 +156,13 @@ ], "replicas": 1, "selector": { - "name": "rails-postgresql-example" + "name": "${NAME}" }, "template": { "metadata": { - "name": "rails-postgresql-example", + "name": "${NAME}", "labels": { - "name": "rails-postgresql-example" + "name": "${NAME}" } }, "spec": { @@ -172,6 +175,22 @@ "containerPort": 8080 } ], + "readinessProbe": { + "timeoutSeconds": 3, + "initialDelaySeconds": 5, + "httpGet": { + "path": "/articles", + "port": 8080 + } + }, + "livenessProbe": { + "timeoutSeconds": 3, + "initialDelaySeconds": 10, + "httpGet": { + "path": "/articles", + "port": 8080 + } + }, "env": [ { "name": "DATABASE_SERVICE_NAME", @@ -202,10 +221,6 @@ "value": "${POSTGRESQL_SHARED_BUFFERS}" }, { - "name": "SECRET_KEY_BASE", - "value": "${SECRET_KEY_BASE}" - }, - { "name": "APPLICATION_DOMAIN", "value": "${APPLICATION_DOMAIN}" }, @@ -221,7 +236,12 @@ "name": "RAILS_ENV", "value": "${RAILS_ENV}" } - ] + ], + "resources": { + "limits": { + "memory": "${MEMORY_LIMIT}" + } + } } ] } @@ -273,7 +293,7 @@ ], "from": { "kind": "ImageStreamTag", - "namespace": "openshift", + "namespace": "${NAMESPACE}", "name": "postgresql:9.4" } } @@ -303,6 +323,20 @@ "containerPort": 5432 } ], + "readinessProbe": { + "timeoutSeconds": 1, + "initialDelaySeconds": 5, + "exec": { + "command": [ "/bin/sh", "-i", "-c", "psql -h 127.0.0.1 -U ${POSTGRESQL_USER} -q -d ${POSTGRESQL_DATABASE} -c 'SELECT 1'"] + } + }, + "livenessProbe": { + "timeoutSeconds": 1, + "initialDelaySeconds": 30, + "tcpSocket": { + "port": 5432 + } + }, "env": [ { "name": "POSTGRESQL_USER", @@ -324,7 +358,12 @@ "name": "POSTGRESQL_SHARED_BUFFERS", "value": "${POSTGRESQL_SHARED_BUFFERS}" } - ] + ], + "resources": { + "limits": { + "memory": "${MEMORY_POSTGRESQL_LIMIT}" + } + } } ] } @@ -334,80 +373,114 @@ ], "parameters": [ { + "name": "NAME", + "displayName": "Name", + "description": "The name assigned to all of the frontend objects defined in this template.", + "required": true, + "value": "rails-postgresql-example" + }, + { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { + "name": "MEMORY_LIMIT", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the Rails container can use.", + "value": "512Mi" + }, + { + "name": "MEMORY_POSTGRESQL_LIMIT", + "displayName": "Memory Limit (PostgreSQL)", + "description": "Maximum amount of memory the PostgreSQL container can use.", + "value": "512Mi" + }, + { "name": "SOURCE_REPOSITORY_URL", - "description": "The URL of the repository with your application source code", + "displayName": "Git Repository URL", + "description": "The URL of the repository with your application source code.", "value": "https://github.com/openshift/rails-ex.git" }, { "name": "SOURCE_REPOSITORY_REF", - "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch" + "displayName": "Git Reference", + "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch." }, { "name": "CONTEXT_DIR", - "description": "Set this to the relative path to your project if it is not in the root of your repository" + "displayName": "Context Directory", + "description": "Set this to the relative path to your project if it is not in the root of your repository." }, { "name": "APPLICATION_DOMAIN", + "displayName": "Application Hostname", "description": "The exposed hostname that will route to the Rails service, if left blank a value will be defaulted.", "value": "" }, { "name": "GITHUB_WEBHOOK_SECRET", - "description": "A secret string used to configure the GitHub webhook", + "displayName": "GitHub Webhook Secret", + "description": "A secret string used to configure the GitHub webhook.", "generate": "expression", "from": "[a-zA-Z0-9]{40}" }, { "name": "SECRET_KEY_BASE", - "description": "Your secret key for verifying the integrity of signed cookies", + "displayName": "Secret Key", + "description": "Your secret key for verifying the integrity of signed cookies.", "generate": "expression", "from": "[a-z0-9]{127}" }, { "name": "APPLICATION_USER", - "description": "The application user that is used within the sample application to authorize access on pages", + "displayName": "Application Username", + "description": "The application user that is used within the sample application to authorize access on pages.", "value": "openshift" }, { "name": "APPLICATION_PASSWORD", - "description": "The application password that is used within the sample application to authorize access on pages", + "displayName": "Application Password", + "description": "The application password that is used within the sample application to authorize access on pages.", "value": "secret" }, { "name": "RAILS_ENV", - "description": "Environment under which the sample application will run. Could be set to production, development or test", + "displayName": "Rails Environment", + "description": "Environment under which the sample application will run. Could be set to production, development or test.", "value": "production" }, { "name": "DATABASE_SERVICE_NAME", - "description": "Database service name", + "displayName": "Database Service Name", "value": "postgresql" }, { "name": "DATABASE_USER", - "description": "database username", + "displayName": "Database Username", "generate": "expression", "from": "user[A-Z0-9]{3}" }, { "name": "DATABASE_PASSWORD", - "description": "database password", + "displayName": "Database Password", "generate": "expression", "from": "[a-zA-Z0-9]{8}" }, { "name": "DATABASE_NAME", - "description": "database name", + "displayName": "Database Name", "value": "root" }, { "name": "POSTGRESQL_MAX_CONNECTIONS", - "description": "database max connections", + "displayName": "Maximum Database Connections", "value": "100" }, { "name": "POSTGRESQL_SHARED_BUFFERS", - "description": "database shared buffers", + "displayName": "Shared Buffer Amount", "value": "12MB" } ] diff --git a/roles/openshift_examples/files/examples/v1.2/db-templates/README.md b/roles/openshift_examples/files/examples/v1.2/db-templates/README.md index b39abf8b9..609f4dec9 100644 --- a/roles/openshift_examples/files/examples/v1.2/db-templates/README.md +++ b/roles/openshift_examples/files/examples/v1.2/db-templates/README.md @@ -9,7 +9,7 @@ the Web Console or the CLI. The examples can also be tweaked to create new templates. -## Ephemeral x Persistent +## Ephemeral vs Persistent For each supported database, there are two template files. diff --git a/roles/openshift_examples/files/examples/v1.2/db-templates/mongodb-ephemeral-template.json b/roles/openshift_examples/files/examples/v1.2/db-templates/mongodb-ephemeral-template.json index 2d0760fdd..227c8d30e 100644 --- a/roles/openshift_examples/files/examples/v1.2/db-templates/mongodb-ephemeral-template.json +++ b/roles/openshift_examples/files/examples/v1.2/db-templates/mongodb-ephemeral-template.json @@ -61,7 +61,7 @@ "from": { "kind": "ImageStreamTag", "name": "mongodb:latest", - "namespace": "openshift" + "namespace": "${NAMESPACE}" }, "lastTriggeredImage": "" } @@ -96,7 +96,7 @@ "timeoutSeconds": 1, "initialDelaySeconds": 3, "exec": { - "command": [ "/bin/sh", "-i", "-c", "mongostat --host 127.0.0.1 -u admin -p ${MONGODB_ADMIN_PASSWORD} -n 1 --noheaders"] + "command": [ "/bin/sh", "-i", "-c", "mongostat --host 127.0.0.1 -u admin -p $MONGODB_ADMIN_PASSWORD -n 1 --noheaders"] } }, "livenessProbe": { @@ -163,44 +163,50 @@ "parameters": [ { "name": "MEMORY_LIMIT", - "displayName": "Memory limit", - "description": "Maximum amount of memory the container can use", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the container can use.", "value": "512Mi" }, { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { "name": "DATABASE_SERVICE_NAME", - "displayName": "Database service name", - "description": "The name of the OpenShift Service exposed for the database", + "displayName": "Database Service Name", + "description": "The name of the OpenShift Service exposed for the database.", "value": "mongodb", "required": true }, { "name": "MONGODB_USER", - "displayName": "MongoDB user", - "description": "Username for MongoDB user that will be used for accessing the database", + "displayName": "MongoDB User", + "description": "Username for MongoDB user that will be used for accessing the database.", "generate": "expression", "from": "user[A-Z0-9]{3}", "required": true }, { "name": "MONGODB_PASSWORD", - "displayName": "MongoDB password", - "description": "Password for the MongoDB user", + "displayName": "MongoDB Password", + "description": "Password for the MongoDB user.", "generate": "expression", "from": "[a-zA-Z0-9]{16}", "required": true }, { "name": "MONGODB_DATABASE", - "displayName": "MongoDB database name", - "description": "Name of the MongoDB database accessed", + "displayName": "MongoDB Database Name", + "description": "Name of the MongoDB database accessed.", "value": "sampledb", "required": true }, { "name": "MONGODB_ADMIN_PASSWORD", - "displayName": "MongoDB admin password", - "description": "Password for the database admin user", + "displayName": "MongoDB Admin Password", + "description": "Password for the database admin user.", "generate": "expression", "from": "[a-zA-Z0-9]{16}", "required": true diff --git a/roles/openshift_examples/files/examples/v1.2/db-templates/mongodb-persistent-template.json b/roles/openshift_examples/files/examples/v1.2/db-templates/mongodb-persistent-template.json index da22a8d1b..672eaaa09 100644 --- a/roles/openshift_examples/files/examples/v1.2/db-templates/mongodb-persistent-template.json +++ b/roles/openshift_examples/files/examples/v1.2/db-templates/mongodb-persistent-template.json @@ -78,7 +78,7 @@ "from": { "kind": "ImageStreamTag", "name": "mongodb:latest", - "namespace": "openshift" + "namespace": "${NAMESPACE}" }, "lastTriggeredImage": "" } @@ -113,7 +113,7 @@ "timeoutSeconds": 1, "initialDelaySeconds": 3, "exec": { - "command": [ "/bin/sh", "-i", "-c", "mongostat --host 127.0.0.1 -u admin -p ${MONGODB_ADMIN_PASSWORD} -n 1 --noheaders"] + "command": [ "/bin/sh", "-i", "-c", "mongostat --host 127.0.0.1 -u admin -p $MONGODB_ADMIN_PASSWORD -n 1 --noheaders"] } }, "livenessProbe": { @@ -180,52 +180,58 @@ "parameters": [ { "name": "MEMORY_LIMIT", - "displayName": "Memory limit", - "description": "Maximum amount of memory the container can use", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the container can use.", "value": "512Mi" }, { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { "name": "DATABASE_SERVICE_NAME", - "displayName": "Database service name", - "description": "The name of the OpenShift Service exposed for the database", + "displayName": "Database Service Name", + "description": "The name of the OpenShift Service exposed for the database.", "value": "mongodb", "required": true }, { "name": "MONGODB_USER", - "displayName": "MongoDB user", - "description": "Username for MongoDB user that will be used for accessing the database", + "displayName": "MongoDB User", + "description": "Username for MongoDB user that will be used for accessing the database.", "generate": "expression", "from": "user[A-Z0-9]{3}", "required": true }, { "name": "MONGODB_PASSWORD", - "displayName": "MongoDB password", - "description": "Password for the MongoDB user", + "displayName": "MongoDB Password", + "description": "Password for the MongoDB user.", "generate": "expression", "from": "[a-zA-Z0-9]{16}", "required": true }, { "name": "MONGODB_DATABASE", - "displayName": "MongoDB database name", - "description": "Name of the MongoDB database accessed", + "displayName": "MongoDB Database Name", + "description": "Name of the MongoDB database accessed.", "value": "sampledb", "required": true }, { "name": "MONGODB_ADMIN_PASSWORD", - "displayName": "MongoDB admin password", - "description": "Password for the database admin user", + "displayName": "MongoDB Admin Password", + "description": "Password for the database admin user.", "generate": "expression", "from": "[a-zA-Z0-9]{16}", "required": true }, { "name": "VOLUME_CAPACITY", - "displayName": "Volume capacity", - "description": "Volume space available for data, e.g. 512Mi, 2Gi", + "displayName": "Volume Capacity", + "description": "Volume space available for data, e.g. 512Mi, 2Gi.", "value": "512Mi", "required": true } diff --git a/roles/openshift_examples/files/examples/v1.2/db-templates/mysql-ephemeral-template.json b/roles/openshift_examples/files/examples/v1.2/db-templates/mysql-ephemeral-template.json index 5c042e7ea..f4c118052 100644 --- a/roles/openshift_examples/files/examples/v1.2/db-templates/mysql-ephemeral-template.json +++ b/roles/openshift_examples/files/examples/v1.2/db-templates/mysql-ephemeral-template.json @@ -61,7 +61,7 @@ "from": { "kind": "ImageStreamTag", "name": "mysql:latest", - "namespace": "openshift" + "namespace": "${NAMESPACE}" }, "lastTriggeredImage": "" } @@ -97,7 +97,7 @@ "initialDelaySeconds": 5, "exec": { "command": [ "/bin/sh", "-i", "-c", - "MYSQL_PWD='${MYSQL_PASSWORD}' mysql -h 127.0.0.1 -u ${MYSQL_USER} -D ${MYSQL_DATABASE} -e 'SELECT 1'"] + "MYSQL_PWD=\"$MYSQL_PASSWORD\" mysql -h 127.0.0.1 -u $MYSQL_USER -D $MYSQL_DATABASE -e 'SELECT 1'"] } }, "livenessProbe": { @@ -160,37 +160,43 @@ "parameters": [ { "name": "MEMORY_LIMIT", - "displayName": "Memory limit", - "description": "Maximum amount of memory the container can use", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the container can use.", "value": "512Mi" }, { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { "name": "DATABASE_SERVICE_NAME", - "displayName": "Database service name", - "description": "The name of the OpenShift Service exposed for the database", + "displayName": "Database Service Name", + "description": "The name of the OpenShift Service exposed for the database.", "value": "mysql", "required": true }, { "name": "MYSQL_USER", - "displayName": "MySQL user", - "description": "Username for MySQL user that will be used for accessing the database", + "displayName": "MySQL User", + "description": "Username for MySQL user that will be used for accessing the database.", "generate": "expression", "from": "user[A-Z0-9]{3}", "required": true }, { "name": "MYSQL_PASSWORD", - "displayName": "MySQL password", - "description": "Password for the MySQL user", + "displayName": "MySQL Password", + "description": "Password for the MySQL user.", "generate": "expression", "from": "[a-zA-Z0-9]{16}", "required": true }, { "name": "MYSQL_DATABASE", - "displayName": "MySQL database name", - "description": "Name of the MySQL database accessed", + "displayName": "MySQL Database Name", + "description": "Name of the MySQL database accessed.", "value": "sampledb", "required": true } diff --git a/roles/openshift_examples/files/examples/v1.2/db-templates/mysql-persistent-template.json b/roles/openshift_examples/files/examples/v1.2/db-templates/mysql-persistent-template.json index d565179a5..d94262dde 100644 --- a/roles/openshift_examples/files/examples/v1.2/db-templates/mysql-persistent-template.json +++ b/roles/openshift_examples/files/examples/v1.2/db-templates/mysql-persistent-template.json @@ -78,7 +78,7 @@ "from": { "kind": "ImageStreamTag", "name": "mysql:latest", - "namespace": "openshift" + "namespace": "${NAMESPACE}" }, "lastTriggeredImage": "" } @@ -114,7 +114,7 @@ "initialDelaySeconds": 5, "exec": { "command": [ "/bin/sh", "-i", "-c", - "MYSQL_PWD='${MYSQL_PASSWORD}' mysql -h 127.0.0.1 -u ${MYSQL_USER} -D ${MYSQL_DATABASE} -e 'SELECT 1'"] + "MYSQL_PWD=\"$MYSQL_PASSWORD\" mysql -h 127.0.0.1 -u $MYSQL_USER -D $MYSQL_DATABASE -e 'SELECT 1'"] } }, "livenessProbe": { @@ -177,44 +177,50 @@ "parameters": [ { "name": "MEMORY_LIMIT", - "displayName": "Memory limit", - "description": "Maximum amount of memory the container can use", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the container can use.", "value": "512Mi" }, { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { "name": "DATABASE_SERVICE_NAME", - "displayName": "Database service name", - "description": "The name of the OpenShift Service exposed for the database", + "displayName": "Database Service Name", + "description": "The name of the OpenShift Service exposed for the database.", "value": "mysql", "required": true }, { "name": "MYSQL_USER", - "displayName": "MySQL user", - "description": "Username for MySQL user that will be used for accessing the database", + "displayName": "MySQL User", + "description": "Username for MySQL user that will be used for accessing the database.", "generate": "expression", "from": "user[A-Z0-9]{3}", "required": true }, { "name": "MYSQL_PASSWORD", - "displayName": "MySQL password", - "description": "Password for the MySQL user", + "displayName": "MySQL Password", + "description": "Password for the MySQL user.", "generate": "expression", "from": "[a-zA-Z0-9]{16}", "required": true }, { "name": "MYSQL_DATABASE", - "displayName": "MySQL database name", - "description": "Name of the MySQL database accessed", + "displayName": "MySQL Database Name", + "description": "Name of the MySQL database accessed.", "value": "sampledb", "required": true }, { "name": "VOLUME_CAPACITY", - "displayName": "Volume capacity", - "description": "Volume space available for data, e.g. 512Mi, 2Gi", + "displayName": "Volume Capacity", + "description": "Volume space available for data, e.g. 512Mi, 2Gi.", "value": "512Mi", "required": true } diff --git a/roles/openshift_examples/files/examples/v1.2/db-templates/postgresql-ephemeral-template.json b/roles/openshift_examples/files/examples/v1.2/db-templates/postgresql-ephemeral-template.json index 8105e487b..c14f3c3df 100644 --- a/roles/openshift_examples/files/examples/v1.2/db-templates/postgresql-ephemeral-template.json +++ b/roles/openshift_examples/files/examples/v1.2/db-templates/postgresql-ephemeral-template.json @@ -61,7 +61,7 @@ "from": { "kind": "ImageStreamTag", "name": "postgresql:latest", - "namespace": "openshift" + "namespace": "${NAMESPACE}" }, "lastTriggeredImage": "" } @@ -96,7 +96,7 @@ "timeoutSeconds": 1, "initialDelaySeconds": 5, "exec": { - "command": [ "/bin/sh", "-i", "-c", "psql -h 127.0.0.1 -U ${POSTGRESQL_USER} -q -d ${POSTGRESQL_DATABASE} -c 'SELECT 1'"] + "command": [ "/bin/sh", "-i", "-c", "psql -h 127.0.0.1 -U $POSTGRESQL_USER -q -d $POSTGRESQL_DATABASE -c 'SELECT 1'"] } }, "livenessProbe": { @@ -159,37 +159,43 @@ "parameters": [ { "name": "MEMORY_LIMIT", - "displayName": "Memory limit", - "description": "Maximum amount of memory the container can use", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the container can use.", "value": "512Mi" }, { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { "name": "DATABASE_SERVICE_NAME", - "displayName": "Database service name", - "description": "The name of the OpenShift Service exposed for the database", + "displayName": "Database Service Name", + "description": "The name of the OpenShift Service exposed for the database.", "value": "postgresql", "required": true }, { "name": "POSTGRESQL_USER", - "displayName": "PostgreSQL user", - "description": "Username for PostgreSQL user that will be used for accessing the database", + "displayName": "PostgreSQL User", + "description": "Username for PostgreSQL user that will be used for accessing the database.", "generate": "expression", "from": "user[A-Z0-9]{3}", "required": true }, { "name": "POSTGRESQL_PASSWORD", - "displayName": "PostgreSQL password", - "description": "Password for the PostgreSQL user", + "displayName": "PostgreSQL Password", + "description": "Password for the PostgreSQL user.", "generate": "expression", "from": "[a-zA-Z0-9]{16}", "required": true }, { "name": "POSTGRESQL_DATABASE", - "displayName": "PostgreSQL database name", - "description": "Name of the PostgreSQL database accessed", + "displayName": "PostgreSQL Database Name", + "description": "Name of the PostgreSQL database accessed.", "value": "sampledb", "required": true } diff --git a/roles/openshift_examples/files/examples/v1.2/db-templates/postgresql-persistent-template.json b/roles/openshift_examples/files/examples/v1.2/db-templates/postgresql-persistent-template.json index 5d7c525c0..5713411ad 100644 --- a/roles/openshift_examples/files/examples/v1.2/db-templates/postgresql-persistent-template.json +++ b/roles/openshift_examples/files/examples/v1.2/db-templates/postgresql-persistent-template.json @@ -78,7 +78,7 @@ "from": { "kind": "ImageStreamTag", "name": "postgresql:latest", - "namespace": "openshift" + "namespace": "${NAMESPACE}" }, "lastTriggeredImage": "" } @@ -113,7 +113,7 @@ "timeoutSeconds": 1, "initialDelaySeconds": 5, "exec": { - "command": [ "/bin/sh", "-i", "-c", "psql -h 127.0.0.1 -U ${POSTGRESQL_USER} -q -d ${POSTGRESQL_DATABASE} -c 'SELECT 1'"] + "command": [ "/bin/sh", "-i", "-c", "psql -h 127.0.0.1 -U $POSTGRESQL_USER -q -d $POSTGRESQL_DATABASE -c 'SELECT 1'"] } }, "livenessProbe": { @@ -138,10 +138,10 @@ } ], "resources": { - "limits": { - "memory": "${MEMORY_LIMIT}" - } - }, + "limits": { + "memory": "${MEMORY_LIMIT}" + } + }, "volumeMounts": [ { "name": "${DATABASE_SERVICE_NAME}-data", @@ -176,44 +176,50 @@ "parameters": [ { "name": "MEMORY_LIMIT", - "displayName": "Memory limit", - "description": "Maximum amount of memory the container can use", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the container can use.", "value": "512Mi" }, { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { "name": "DATABASE_SERVICE_NAME", - "displayName": "Database service name", - "description": "The name of the OpenShift Service exposed for the database", + "displayName": "Database Service Name", + "description": "The name of the OpenShift Service exposed for the database.", "value": "postgresql", "required": true }, { "name": "POSTGRESQL_USER", - "displayName": "PostgreSQL user", - "description": "Username for PostgreSQL user that will be used for accessing the database", + "displayName": "PostgreSQL User", + "description": "Username for PostgreSQL user that will be used for accessing the database.", "generate": "expression", "from": "user[A-Z0-9]{3}", "required": true }, { "name": "POSTGRESQL_PASSWORD", - "displayName": "PostgreSQL password", - "description": "Password for the PostgreSQL user", + "displayName": "PostgreSQL Password", + "description": "Password for the PostgreSQL user.", "generate": "expression", "from": "[a-zA-Z0-9]{16}", "required": true }, { "name": "POSTGRESQL_DATABASE", - "displayName": "PostgreSQL database name", - "description": "Name of the PostgreSQL database accessed", + "displayName": "PostgreSQL Database Name", + "description": "Name of the PostgreSQL database accessed.", "value": "sampledb", "required": true }, { "name": "VOLUME_CAPACITY", - "displayName": "Volume capacity", - "description": "Volume space available for data, e.g. 512Mi, 2Gi", + "displayName": "Volume Capacity", + "description": "Volume space available for data, e.g. 512Mi, 2Gi.", "value": "512Mi", "required": true } diff --git a/roles/openshift_examples/files/examples/v1.2/infrastructure-templates/origin/logging-deployer.yaml b/roles/openshift_examples/files/examples/v1.2/infrastructure-templates/origin/logging-deployer.yaml index 4c798e148..9257b1f28 100644 --- a/roles/openshift_examples/files/examples/v1.2/infrastructure-templates/origin/logging-deployer.yaml +++ b/roles/openshift_examples/files/examples/v1.2/infrastructure-templates/origin/logging-deployer.yaml @@ -69,6 +69,8 @@ objects: value: ${ES_OPS_RECOVER_EXPECTED_NODES} - name: ES_OPS_RECOVER_AFTER_TIME value: ${ES_OPS_RECOVER_AFTER_TIME} + - name: FLUENTD_NODESELECTOR + value: ${FLUENTD_NODESELECTOR} dnsPolicy: ClusterFirst restartPolicy: Never serviceAccount: logging-deployer @@ -148,4 +150,7 @@ parameters: description: "Timeout for *expected* ops nodes to be present when cluster is recovering from a full restart." name: ES_OPS_RECOVER_AFTER_TIME value: "5m" - +- + description: "The nodeSelector used for the Fluentd DaemonSet." + name: FLUENTD_NODESELECTOR + value: "logging-infra-fluentd=true" diff --git a/roles/openshift_examples/files/examples/v1.2/quickstart-templates/cakephp-mysql.json b/roles/openshift_examples/files/examples/v1.2/quickstart-templates/cakephp-mysql.json index 9e0ae218d..922e5bed8 100644 --- a/roles/openshift_examples/files/examples/v1.2/quickstart-templates/cakephp-mysql.json +++ b/roles/openshift_examples/files/examples/v1.2/quickstart-templates/cakephp-mysql.json @@ -17,7 +17,7 @@ "kind": "Service", "apiVersion": "v1", "metadata": { - "name": "cakephp-mysql-example", + "name": "${NAME}", "annotations": { "description": "Exposes and load balances the application pods" } @@ -31,7 +31,7 @@ } ], "selector": { - "name": "cakephp-mysql-example" + "name": "${NAME}" } } }, @@ -39,13 +39,13 @@ "kind": "Route", "apiVersion": "v1", "metadata": { - "name": "cakephp-mysql-example" + "name": "${NAME}" }, "spec": { "host": "${APPLICATION_DOMAIN}", "to": { "kind": "Service", - "name": "cakephp-mysql-example" + "name": "${NAME}" } } }, @@ -53,7 +53,7 @@ "kind": "ImageStream", "apiVersion": "v1", "metadata": { - "name": "cakephp-mysql-example", + "name": "${NAME}", "annotations": { "description": "Keeps track of changes in the application image" } @@ -63,7 +63,7 @@ "kind": "BuildConfig", "apiVersion": "v1", "metadata": { - "name": "cakephp-mysql-example", + "name": "${NAME}", "annotations": { "description": "Defines how to build the application" } @@ -82,7 +82,7 @@ "sourceStrategy": { "from": { "kind": "ImageStreamTag", - "namespace": "openshift", + "namespace": "${NAMESPACE}", "name": "php:5.6" } } @@ -90,7 +90,7 @@ "output": { "to": { "kind": "ImageStreamTag", - "name": "cakephp-mysql-example:latest" + "name": "${NAME}:latest" } }, "triggers": [ @@ -113,7 +113,7 @@ "kind": "DeploymentConfig", "apiVersion": "v1", "metadata": { - "name": "cakephp-mysql-example", + "name": "${NAME}", "annotations": { "description": "Defines how to deploy the application server" } @@ -123,7 +123,7 @@ "type": "Rolling", "recreateParams": { "pre": { - "failurePolicy": "Abort", + "failurePolicy": "Retry", "execNewPod": { "command": [ "./migrate-database.sh" @@ -143,7 +143,7 @@ ], "from": { "kind": "ImageStreamTag", - "name": "cakephp-mysql-example:latest" + "name": "${NAME}:latest" } } }, @@ -153,13 +153,13 @@ ], "replicas": 1, "selector": { - "name": "cakephp-mysql-example" + "name": "${NAME}" }, "template": { "metadata": { - "name": "cakephp-mysql-example", + "name": "${NAME}", "labels": { - "name": "cakephp-mysql-example" + "name": "${NAME}" } }, "spec": { @@ -172,6 +172,22 @@ "containerPort": 8080 } ], + "readinessProbe": { + "timeoutSeconds": 3, + "initialDelaySeconds": 3, + "httpGet": { + "path": "/health.php", + "port": 8080 + } + }, + "livenessProbe": { + "timeoutSeconds": 3, + "initialDelaySeconds": 30, + "httpGet": { + "path": "/", + "port": 8080 + } + }, "env": [ { "name": "DATABASE_SERVICE_NAME", @@ -266,7 +282,7 @@ ], "from": { "kind": "ImageStreamTag", - "namespace": "openshift", + "namespace": "${NAMESPACE}", "name": "mysql:5.6" } } @@ -296,24 +312,38 @@ "containerPort": 3306 } ], - "env": [ - { - "name": "MYSQL_USER", - "value": "${DATABASE_USER}" - }, - { - "name": "MYSQL_PASSWORD", - "value": "${DATABASE_PASSWORD}" - }, - { - "name": "MYSQL_DATABASE", - "value": "${DATABASE_NAME}" + "readinessProbe": { + "timeoutSeconds": 1, + "initialDelaySeconds": 5, + "exec": { + "command": [ "/bin/sh", "-i", "-c", "MYSQL_PWD='${DATABASE_PASSWORD}' mysql -h 127.0.0.1 -u ${DATABASE_USER} -D ${DATABASE_NAME} -e 'SELECT 1'" ] } + }, + "livenessProbe": { + "timeoutSeconds": 1, + "initialDelaySeconds": 30, + "tcpSocket": { + "port": 3306 + } + }, + "env": [ + { + "name": "MYSQL_USER", + "value": "${DATABASE_USER}" + }, + { + "name": "MYSQL_PASSWORD", + "value": "${DATABASE_PASSWORD}" + }, + { + "name": "MYSQL_DATABASE", + "value": "${DATABASE_NAME}" + } ], "resources": { - "limits": { - "memory": "${MEMORY_MYSQL_LIMIT}" - } + "limits": { + "memory": "${MEMORY_MYSQL_LIMIT}" + } } } ] @@ -324,101 +354,110 @@ ], "parameters": [ { + "name": "NAME", + "displayName": "Name", + "description": "The name assigned to all of the frontend objects defined in this template.", + "required": true, + "value": "cakephp-mysql-example" + }, + { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { "name": "MEMORY_LIMIT", - "displayName": "Memory limit", - "description": "Maximum amount of memory the CakePHP container can use", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the CakePHP container can use.", "value": "512Mi" }, { "name": "MEMORY_MYSQL_LIMIT", - "displayName": "Memory limit", - "description": "Maximum amount of memory the MySQL container can use", + "displayName": "Memory Limit (MySQL)", + "description": "Maximum amount of memory the MySQL container can use.", "value": "512Mi" }, { "name": "SOURCE_REPOSITORY_URL", - "displayName": "Source repository URL", - "description": "The URL of the repository with your application source code", + "displayName": "Git Repository URL", + "description": "The URL of the repository with your application source code.", "value": "https://github.com/openshift/cakephp-ex.git" }, { "name": "SOURCE_REPOSITORY_REF", - "displayName": "Source repository reference", - "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch" + "displayName": "Git Reference", + "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch." }, { "name": "CONTEXT_DIR", - "displayName": "Context directory", - "description": "Set this to the relative path to your project if it is not in the root of your repository" + "displayName": "Context Directory", + "description": "Set this to the relative path to your project if it is not in the root of your repository." }, { "name": "APPLICATION_DOMAIN", - "displayName": "Application domain", + "displayName": "Application Hostname", "description": "The exposed hostname that will route to the CakePHP service, if left blank a value will be defaulted.", "value": "" }, { "name": "GITHUB_WEBHOOK_SECRET", - "displayName": "GitHub webhook secret", - "description": "A secret string used to configure the GitHub webhook", + "displayName": "GitHub Webhook Secret", + "description": "A secret string used to configure the GitHub webhook.", "generate": "expression", "from": "[a-zA-Z0-9]{40}" }, { "name": "DATABASE_SERVICE_NAME", - "displayName": "Database service name", - "description": "Database service name", + "displayName": "Database Service Name", "value": "mysql" }, { "name": "DATABASE_ENGINE", - "displayName": "Database engine", - "description": "Database engine: postgresql, mysql or sqlite (default)", + "displayName": "Database Engine", + "description": "Database engine: postgresql, mysql or sqlite (default).", "value": "mysql" }, { "name": "DATABASE_NAME", - "displayName": "Database name", - "description": "Database name", + "displayName": "Database Name", "value": "default" }, { "name": "DATABASE_USER", - "displayName": "Database user", - "description": "Database user name", + "displayName": "Database User", "value": "cakephp" }, { "name": "DATABASE_PASSWORD", - "displayName": "Database password", - "description": "Database user password", + "displayName": "Database Password", "generate": "expression", "from": "[a-zA-Z0-9]{16}" }, { "name": "CAKEPHP_SECRET_TOKEN", "displayName": "CakePHP secret token", - "description": "Set this to a long random string", + "description": "Set this to a long random string.", "generate": "expression", "from": "[\\w]{50}" }, { "name": "CAKEPHP_SECURITY_SALT", - "displayName": "CakePHP security salt", - "description": "Security salt for session hash", + "displayName": "CakePHP Security Salt", + "description": "Security salt for session hash.", "generate": "expression", "from": "[a-zA-Z0-9]{40}" }, { "name": "CAKEPHP_SECURITY_CIPHER_SEED", - "displayName": "CakePHP security cipher seed", - "description": "Security cipher seed for session hash", + "displayName": "CakePHP Security Cipher Seed", + "description": "Security cipher seed for session hash.", "generate": "expression", "from": "[0-9]{30}" }, { "name": "OPCACHE_REVALIDATE_FREQ", - "displayName": "OPcache revalidation frequency", + "displayName": "OPcache Revalidation Frequency", "description": "How often to check script timestamps for updates, in seconds. 0 will result in OPcache checking for updates on every request.", "value": "2" } diff --git a/roles/openshift_examples/files/examples/v1.2/quickstart-templates/cakephp.json b/roles/openshift_examples/files/examples/v1.2/quickstart-templates/cakephp.json index d29c446e5..780faec55 100644 --- a/roles/openshift_examples/files/examples/v1.2/quickstart-templates/cakephp.json +++ b/roles/openshift_examples/files/examples/v1.2/quickstart-templates/cakephp.json @@ -17,7 +17,7 @@ "kind": "Service", "apiVersion": "v1", "metadata": { - "name": "cakephp-example", + "name": "${NAME}", "annotations": { "description": "Exposes and load balances the application pods" } @@ -31,7 +31,7 @@ } ], "selector": { - "name": "cakephp-example" + "name": "${NAME}" } } }, @@ -39,13 +39,13 @@ "kind": "Route", "apiVersion": "v1", "metadata": { - "name": "cakephp-example" + "name": "${NAME}" }, "spec": { "host": "${APPLICATION_DOMAIN}", "to": { "kind": "Service", - "name": "cakephp-example" + "name": "${NAME}" } } }, @@ -53,7 +53,7 @@ "kind": "ImageStream", "apiVersion": "v1", "metadata": { - "name": "cakephp-example", + "name": "${NAME}", "annotations": { "description": "Keeps track of changes in the application image" } @@ -63,7 +63,7 @@ "kind": "BuildConfig", "apiVersion": "v1", "metadata": { - "name": "cakephp-example", + "name": "${NAME}", "annotations": { "description": "Defines how to build the application" } @@ -82,7 +82,7 @@ "sourceStrategy": { "from": { "kind": "ImageStreamTag", - "namespace": "openshift", + "namespace": "${NAMESPACE}", "name": "php:5.6" } } @@ -90,7 +90,7 @@ "output": { "to": { "kind": "ImageStreamTag", - "name": "cakephp-example:latest" + "name": "${NAME}:latest" } }, "triggers": [ @@ -113,7 +113,7 @@ "kind": "DeploymentConfig", "apiVersion": "v1", "metadata": { - "name": "cakephp-example", + "name": "${NAME}", "annotations": { "description": "Defines how to deploy the application server" } @@ -132,7 +132,7 @@ ], "from": { "kind": "ImageStreamTag", - "name": "cakephp-example:latest" + "name": "${NAME}:latest" } } }, @@ -142,13 +142,13 @@ ], "replicas": 1, "selector": { - "name": "cakephp-example" + "name": "${NAME}" }, "template": { "metadata": { - "name": "cakephp-example", + "name": "${NAME}", "labels": { - "name": "cakephp-example" + "name": "${NAME}" } }, "spec": { @@ -161,6 +161,22 @@ "containerPort": 8080 } ], + "readinessProbe": { + "timeoutSeconds": 3, + "initialDelaySeconds": 3, + "httpGet": { + "path": "/", + "port": 8080 + } + }, + "livenessProbe": { + "timeoutSeconds": 3, + "initialDelaySeconds": 30, + "httpGet": { + "path": "/", + "port": 8080 + } + }, "env": [ { "name": "DATABASE_SERVICE_NAME", @@ -213,89 +229,98 @@ ], "parameters": [ { + "name": "NAME", + "displayName": "Name", + "description": "The name assigned to all of the frontend objects defined in this template.", + "required": true, + "value": "cakephp-example" + }, + { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { "name": "MEMORY_LIMIT", - "displayName": "Memory limit", - "description": "Maximum amount of memory the container can use", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the container can use.", "value": "512Mi" }, { "name": "SOURCE_REPOSITORY_URL", - "displayName": "Source repository URL", - "description": "The URL of the repository with your application source code", + "displayName": "Git Repository URL", + "description": "The URL of the repository with your application source code.", "value": "https://github.com/openshift/cakephp-ex.git" }, { "name": "SOURCE_REPOSITORY_REF", - "displayName": "Source repository reference", - "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch" + "displayName": "Git Reference", + "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch." }, { "name": "CONTEXT_DIR", - "displayName": "Context directory", - "description": "Set this to the relative path to your project if it is not in the root of your repository" + "displayName": "Context Directory", + "description": "Set this to the relative path to your project if it is not in the root of your repository." }, { "name": "APPLICATION_DOMAIN", - "displayName": "Application domain", + "displayName": "Application Hostname", "description": "The exposed hostname that will route to the CakePHP service, if left blank a value will be defaulted.", "value": "" }, { "name": "GITHUB_WEBHOOK_SECRET", - "displayName": "GitHub webhook secret", - "description": "A secret string used to configure the GitHub webhook", + "displayName": "GitHub Webhook Secret", + "description": "A secret string used to configure the GitHub webhook.", "generate": "expression", "from": "[a-zA-Z0-9]{40}" }, { "name": "DATABASE_SERVICE_NAME", - "displayName": "Database service name", - "description": "Database service name" + "displayName": "Database Service Name" }, { "name": "DATABASE_ENGINE", - "displayName": "Database engine", - "description": "Database engine: postgresql, mysql or sqlite (default)" + "displayName": "Database Engine", + "description": "Database engine: postgresql, mysql or sqlite (default)." }, { "name": "DATABASE_NAME", - "displayName": "Database name", - "description": "Database name" + "displayName": "Database Name" }, { "name": "DATABASE_USER", - "displayName": "Database user", - "description": "Database user name" + "displayName": "Database User" }, { "name": "DATABASE_PASSWORD", - "displayName": "Database password", - "description": "Database user password" + "displayName": "Database Password" }, { "name": "CAKEPHP_SECRET_TOKEN", - "displayName": "CakePHP secret token", - "description": "Set this to a long random string", + "displayName": "CakePHP Secret Token", + "description": "Set this to a long random string.", "generate": "expression", "from": "[\\w]{50}" }, { "name": "CAKEPHP_SECURITY_SALT", - "displayName": "CakePHP security salt", - "description": "Security salt for session hash", + "displayName": "CakePHP Security Salt", + "description": "Security salt for session hash.", "generate": "expression", "from": "[a-zA-Z0-9]{40}" }, { "name": "CAKEPHP_SECURITY_CIPHER_SEED", - "displayName": "CakePHP security cipher seed", - "description": "Security cipher seed for session hash", + "displayName": "CakePHP Security Cipher Seed", + "description": "Security cipher seed for session hash.", "generate": "expression", "from": "[0-9]{30}" }, { "name": "OPCACHE_REVALIDATE_FREQ", - "displayName": "OPcache revalidation frequency", + "displayName": "OPcache Revalidation Frequency", "description": "How often to check script timestamps for updates, in seconds. 0 will result in OPcache checking for updates on every request.", "value": "2" } diff --git a/roles/openshift_examples/files/examples/v1.2/quickstart-templates/dancer-mysql.json b/roles/openshift_examples/files/examples/v1.2/quickstart-templates/dancer-mysql.json index 2e5c8021f..c0fc02ae4 100644 --- a/roles/openshift_examples/files/examples/v1.2/quickstart-templates/dancer-mysql.json +++ b/roles/openshift_examples/files/examples/v1.2/quickstart-templates/dancer-mysql.json @@ -17,7 +17,7 @@ "kind": "Service", "apiVersion": "v1", "metadata": { - "name": "dancer-mysql-example", + "name": "${NAME}", "annotations": { "description": "Exposes and load balances the application pods" } @@ -31,7 +31,7 @@ } ], "selector": { - "name": "dancer-mysql-example" + "name": "${NAME}" } } }, @@ -39,13 +39,13 @@ "kind": "Route", "apiVersion": "v1", "metadata": { - "name": "dancer-mysql-example" + "name": "${NAME}" }, "spec": { "host": "${APPLICATION_DOMAIN}", "to": { "kind": "Service", - "name": "dancer-mysql-example" + "name": "${NAME}" } } }, @@ -53,7 +53,7 @@ "kind": "ImageStream", "apiVersion": "v1", "metadata": { - "name": "dancer-mysql-example", + "name": "${NAME}", "annotations": { "description": "Keeps track of changes in the application image" } @@ -63,7 +63,7 @@ "kind": "BuildConfig", "apiVersion": "v1", "metadata": { - "name": "dancer-mysql-example", + "name": "${NAME}", "annotations": { "description": "Defines how to build the application" } @@ -82,7 +82,7 @@ "sourceStrategy": { "from": { "kind": "ImageStreamTag", - "namespace": "openshift", + "namespace": "${NAMESPACE}", "name": "perl:5.20" } } @@ -90,7 +90,7 @@ "output": { "to": { "kind": "ImageStreamTag", - "name": "dancer-mysql-example:latest" + "name": "${NAME}:latest" } }, "triggers": [ @@ -113,7 +113,7 @@ "kind": "DeploymentConfig", "apiVersion": "v1", "metadata": { - "name": "dancer-mysql-example", + "name": "${NAME}", "annotations": { "description": "Defines how to deploy the application server" } @@ -129,7 +129,7 @@ ], "from": { "kind": "ImageStreamTag", - "name": "dancer-mysql-example:latest" + "name": "${NAME}:latest" } } }, @@ -139,13 +139,13 @@ ], "replicas": 1, "selector": { - "name": "dancer-mysql-example" + "name": "${NAME}" }, "template": { "metadata": { - "name": "dancer-mysql-example", + "name": "${NAME}", "labels": { - "name": "dancer-mysql-example" + "name": "${NAME}" } }, "spec": { @@ -256,7 +256,7 @@ ], "from": { "kind": "ImageStreamTag", - "namespace": "openshift", + "namespace": "${NAMESPACE}", "name": "mysql:5.6" } } @@ -328,89 +328,103 @@ ], "parameters": [ { + "name": "NAME", + "displayName": "Name", + "description": "The name assigned to all of the frontend objects defined in this template.", + "required": true, + "value": "dancer-mysql-example" + }, + { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { "name": "MEMORY_LIMIT", - "displayName": "Memory limit", - "description": "Maximum amount of memory the Perl Dancer container can use", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the Perl Dancer container can use.", "value": "512Mi" }, { "name": "MEMORY_MYSQL_LIMIT", - "displayName": "Memory limit", - "description": "Maximum amount of memory the MySQL container can use", + "displayName": "Memory Limit (MySQL)", + "description": "Maximum amount of memory the MySQL container can use.", "value": "512Mi" }, { "name": "SOURCE_REPOSITORY_URL", - "displayName": "Source repository URL", - "description": "The URL of the repository with your application source code", + "displayName": "Git Repository URL", + "description": "The URL of the repository with your application source code.", "value": "https://github.com/openshift/dancer-ex.git" }, { "name": "SOURCE_REPOSITORY_REF", - "displayName": "Source repository reference", - "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch" + "displayName": "Git Reference", + "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch." }, { "name": "CONTEXT_DIR", - "displayName": "Context directory", - "description": "Set this to the relative path to your project if it is not in the root of your repository" + "displayName": "Context Directory", + "description": "Set this to the relative path to your project if it is not in the root of your repository." }, { "name": "APPLICATION_DOMAIN", - "displayName": "Application domain", + "displayName": "Application Hostname", "description": "The exposed hostname that will route to the Dancer service, if left blank a value will be defaulted.", "value": "" }, { "name": "GITHUB_WEBHOOK_SECRET", - "displayName": "GitHub webhook secret", - "description": "A secret string used to configure the GitHub webhook", + "displayName": "GitHub Webhook Secret", + "description": "A secret string used to configure the GitHub webhook.", "generate": "expression", "from": "[a-zA-Z0-9]{40}" }, { "name": "ADMIN_USERNAME", - "displayName": "Administrator username", - "description": "administrator username", + "displayName": "Administrator Username", "generate": "expression", "from": "admin[A-Z0-9]{3}" }, { "name": "ADMIN_PASSWORD", - "description": "administrator password", + "displayName": "Administrator Password", "generate": "expression", "from": "[a-zA-Z0-9]{8}" }, { "name": "DATABASE_SERVICE_NAME", - "description": "Database service name", + "displayName": "Database Service Name", "value": "database" }, { "name": "DATABASE_USER", - "description": "database username", + "displayName": "Database Username", "generate": "expression", "from": "user[A-Z0-9]{3}" }, { "name": "DATABASE_PASSWORD", - "description": "database password", + "displayName": "Database Password", "generate": "expression", "from": "[a-zA-Z0-9]{8}" }, { "name": "DATABASE_NAME", - "description": "database name", + "displayName": "Database Name", "value": "sampledb" }, { "name": "PERL_APACHE2_RELOAD", - "description": "Set this to \"true\" to enable automatic reloading of modified Perl modules", + "displayName": "Perl Module Reload", + "description": "Set this to \"true\" to enable automatic reloading of modified Perl modules.", "value": "" }, { "name": "SECRET_KEY_BASE", - "description": "Your secret key for verifying the integrity of signed cookies", + "displayName": "Secret Key", + "description": "Your secret key for verifying the integrity of signed cookies.", "generate": "expression", "from": "[a-z0-9]{127}" } diff --git a/roles/openshift_examples/files/examples/v1.2/quickstart-templates/dancer.json b/roles/openshift_examples/files/examples/v1.2/quickstart-templates/dancer.json index 83b010e95..1ea5a21a0 100644 --- a/roles/openshift_examples/files/examples/v1.2/quickstart-templates/dancer.json +++ b/roles/openshift_examples/files/examples/v1.2/quickstart-templates/dancer.json @@ -17,7 +17,7 @@ "kind": "Service", "apiVersion": "v1", "metadata": { - "name": "dancer-example", + "name": "${NAME}", "annotations": { "description": "Exposes and load balances the application pods" } @@ -31,7 +31,7 @@ } ], "selector": { - "name": "dancer-example" + "name": "${NAME}" } } }, @@ -39,13 +39,13 @@ "kind": "Route", "apiVersion": "v1", "metadata": { - "name": "dancer-example" + "name": "${NAME}" }, "spec": { "host": "${APPLICATION_DOMAIN}", "to": { "kind": "Service", - "name": "dancer-example" + "name": "${NAME}" } } }, @@ -53,7 +53,7 @@ "kind": "ImageStream", "apiVersion": "v1", "metadata": { - "name": "dancer-example", + "name": "${NAME}", "annotations": { "description": "Keeps track of changes in the application image" } @@ -63,7 +63,7 @@ "kind": "BuildConfig", "apiVersion": "v1", "metadata": { - "name": "dancer-example", + "name": "${NAME}", "annotations": { "description": "Defines how to build the application" } @@ -82,7 +82,7 @@ "sourceStrategy": { "from": { "kind": "ImageStreamTag", - "namespace": "openshift", + "namespace": "${NAMESPACE}", "name": "perl:5.20" } } @@ -90,7 +90,7 @@ "output": { "to": { "kind": "ImageStreamTag", - "name": "dancer-example:latest" + "name": "${NAME}:latest" } }, "triggers": [ @@ -113,7 +113,7 @@ "kind": "DeploymentConfig", "apiVersion": "v1", "metadata": { - "name": "dancer-example", + "name": "${NAME}", "annotations": { "description": "Defines how to deploy the application server" } @@ -132,7 +132,7 @@ ], "from": { "kind": "ImageStreamTag", - "name": "dancer-example:latest" + "name": "${NAME}:latest" } } }, @@ -142,13 +142,13 @@ ], "replicas": 1, "selector": { - "name": "dancer-example" + "name": "${NAME}" }, "template": { "metadata": { - "name": "dancer-example", + "name": "${NAME}", "labels": { - "name": "dancer-example" + "name": "${NAME}" } }, "spec": { @@ -197,51 +197,64 @@ ], "parameters": [ { + "name": "NAME", + "displayName": "Name", + "description": "The name assigned to all of the frontend objects defined in this template.", + "required": true, + "value": "dancer-example" + }, + { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { "name": "MEMORY_LIMIT", - "displayName": "Memory limit", - "description": "Maximum amount of memory the container can use", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the container can use.", "value": "512Mi" }, { "name": "SOURCE_REPOSITORY_URL", - "displayName": "Source repository URL", - "description": "The URL of the repository with your application source code", + "displayName": "Git Repository URL", + "description": "The URL of the repository with your application source code.", "value": "https://github.com/openshift/dancer-ex.git" }, { "name": "SOURCE_REPOSITORY_REF", - "displayName": "Source repository reference", - "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch" + "displayName": "Git Reference", + "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch." }, { "name": "CONTEXT_DIR", - "displayName": "Context directory", - "description": "Set this to the relative path to your project if it is not in the root of your repository" + "displayName": "Context Directory", + "description": "Set this to the relative path to your project if it is not in the root of your repository." }, { "name": "APPLICATION_DOMAIN", - "displayName": "Application domain", + "displayName": "Application Hostname", "description": "The exposed hostname that will route to the Dancer service, if left blank a value will be defaulted.", "value": "" }, { "name": "GITHUB_WEBHOOK_SECRET", - "displayName": "GitHub webhook secret", - "description": "A secret string used to configure the GitHub webhook", + "displayName": "GitHub Webhook Secret", + "description": "A secret string used to configure the GitHub webhook.", "generate": "expression", "from": "[a-zA-Z0-9]{40}" }, { "name": "SECRET_KEY_BASE", - "displayName": "Secret key", - "description": "Your secret key for verifying the integrity of signed cookies", + "displayName": "Secret Key", + "description": "Your secret key for verifying the integrity of signed cookies.", "generate": "expression", "from": "[a-z0-9]{127}" }, { "name": "PERL_APACHE2_RELOAD", - "displayName": "Perl module reload", - "description": "Set this to \"true\" to enable automatic reloading of modified Perl modules", + "displayName": "Perl Module Reload", + "description": "Set this to \"true\" to enable automatic reloading of modified Perl modules.", "value": "" } ] diff --git a/roles/openshift_examples/files/examples/v1.2/quickstart-templates/django-postgresql.json b/roles/openshift_examples/files/examples/v1.2/quickstart-templates/django-postgresql.json index 5bf3235c6..844201e7c 100644 --- a/roles/openshift_examples/files/examples/v1.2/quickstart-templates/django-postgresql.json +++ b/roles/openshift_examples/files/examples/v1.2/quickstart-templates/django-postgresql.json @@ -17,7 +17,7 @@ "kind": "Service", "apiVersion": "v1", "metadata": { - "name": "django-psql-example", + "name": "${NAME}", "annotations": { "description": "Exposes and load balances the application pods" } @@ -31,7 +31,7 @@ } ], "selector": { - "name": "django-psql-example" + "name": "${NAME}" } } }, @@ -39,13 +39,13 @@ "kind": "Route", "apiVersion": "v1", "metadata": { - "name": "django-psql-example" + "name": "${NAME}" }, "spec": { "host": "${APPLICATION_DOMAIN}", "to": { "kind": "Service", - "name": "django-psql-example" + "name": "${NAME}" } } }, @@ -53,7 +53,7 @@ "kind": "ImageStream", "apiVersion": "v1", "metadata": { - "name": "django-psql-example", + "name": "${NAME}", "annotations": { "description": "Keeps track of changes in the application image" } @@ -63,7 +63,7 @@ "kind": "BuildConfig", "apiVersion": "v1", "metadata": { - "name": "django-psql-example", + "name": "${NAME}", "annotations": { "description": "Defines how to build the application" } @@ -82,7 +82,7 @@ "sourceStrategy": { "from": { "kind": "ImageStreamTag", - "namespace": "openshift", + "namespace": "${NAMESPACE}", "name": "python:3.4" } } @@ -90,7 +90,7 @@ "output": { "to": { "kind": "ImageStreamTag", - "name": "django-psql-example:latest" + "name": "${NAME}:latest" } }, "triggers": [ @@ -116,7 +116,7 @@ "kind": "DeploymentConfig", "apiVersion": "v1", "metadata": { - "name": "django-psql-example", + "name": "${NAME}", "annotations": { "description": "Defines how to deploy the application server" } @@ -135,7 +135,7 @@ ], "from": { "kind": "ImageStreamTag", - "name": "django-psql-example:latest" + "name": "${NAME}:latest" } } }, @@ -145,13 +145,13 @@ ], "replicas": 1, "selector": { - "name": "django-psql-example" + "name": "${NAME}" }, "template": { "metadata": { - "name": "django-psql-example", + "name": "${NAME}", "labels": { - "name": "django-psql-example" + "name": "${NAME}" } }, "spec": { @@ -266,7 +266,7 @@ ], "from": { "kind": "ImageStreamTag", - "namespace": "openshift", + "namespace": "${NAMESPACE}", "name": "postgresql:9.4" } } @@ -338,86 +338,95 @@ ], "parameters": [ { + "name": "NAME", + "displayName": "Name", + "description": "The name assigned to all of the frontend objects defined in this template.", + "required": true, + "value": "django-psql-example" + }, + { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { "name": "MEMORY_LIMIT", - "displayName": "Memory limit", - "description": "Maximum amount of memory the Django container can use", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the Django container can use.", "value": "512Mi" }, { "name": "MEMORY_POSTGRESQL_LIMIT", - "displayName": "Memory limit", - "description": "Maximum amount of memory the PostgreSQL container can use", + "displayName": "Memory Limit (PostgreSQL)", + "description": "Maximum amount of memory the PostgreSQL container can use.", "value": "512Mi" }, { "name": "SOURCE_REPOSITORY_URL", - "displayName": "Source repository URL", - "description": "The URL of the repository with your application source code", + "displayName": "Git Repository URL", + "description": "The URL of the repository with your application source code.", "value": "https://github.com/openshift/django-ex.git" }, { "name": "SOURCE_REPOSITORY_REF", - "displayName": "Source repository reference", - "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch" + "displayName": "Git Reference", + "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch." }, { "name": "CONTEXT_DIR", - "displayName": "Context directory", - "description": "Set this to the relative path to your project if it is not in the root of your repository" + "displayName": "Context Directory", + "description": "Set this to the relative path to your project if it is not in the root of your repository." }, { "name": "APPLICATION_DOMAIN", - "displayName": "Application hostname", + "displayName": "Application Hostname", "description": "The exposed hostname that will route to the Django service, if left blank a value will be defaulted.", "value": "" }, { "name": "GITHUB_WEBHOOK_SECRET", - "displayName": "GitHub webhook secret", - "description": "A secret string used to configure the GitHub webhook", + "displayName": "GitHub Webhook Secret", + "description": "A secret string used to configure the GitHub webhook.", "generate": "expression", "from": "[a-zA-Z0-9]{40}" }, { "name": "DATABASE_SERVICE_NAME", - "displayName": "Database service name", - "description": "Database service name", + "displayName": "Database Service Name", "value": "postgresql" }, { "name": "DATABASE_ENGINE", - "displayName": "Database engine", - "description": "Database engine: postgresql, mysql or sqlite (default)", + "displayName": "Database Engine", + "description": "Database engine: postgresql, mysql or sqlite (default).", "value": "postgresql" }, { "name": "DATABASE_NAME", - "displayName": "Database name", - "description": "Database name", + "displayName": "Database Name", "value": "default" }, { "name": "DATABASE_USER", - "displayName": "Database user name", - "description": "Database user name", + "displayName": "Database Username", "value": "django" }, { "name": "DATABASE_PASSWORD", - "displayName": "Database user password", - "description": "Database user password", + "displayName": "Database User Password", "generate": "expression", "from": "[a-zA-Z0-9]{16}" }, { "name": "APP_CONFIG", - "displayName": "Application configuration file path", - "description": "Relative path to Gunicorn configuration file (optional)" + "displayName": "Application Configuration File Path", + "description": "Relative path to Gunicorn configuration file (optional)." }, { "name": "DJANGO_SECRET_KEY", - "displayName": "Djange secret key", - "description": "Set this to a long random string", + "displayName": "Djange Secret Key", + "description": "Set this to a long random string.", "generate": "expression", "from": "[\\w]{50}" } diff --git a/roles/openshift_examples/files/examples/v1.2/quickstart-templates/django.json b/roles/openshift_examples/files/examples/v1.2/quickstart-templates/django.json index 1ddc9e088..38ef694f8 100644 --- a/roles/openshift_examples/files/examples/v1.2/quickstart-templates/django.json +++ b/roles/openshift_examples/files/examples/v1.2/quickstart-templates/django.json @@ -17,7 +17,7 @@ "kind": "Service", "apiVersion": "v1", "metadata": { - "name": "django-example", + "name": "${NAME}", "annotations": { "description": "Exposes and load balances the application pods" } @@ -31,7 +31,7 @@ } ], "selector": { - "name": "django-example" + "name": "${NAME}" } } }, @@ -39,13 +39,13 @@ "kind": "Route", "apiVersion": "v1", "metadata": { - "name": "django-example" + "name": "${NAME}" }, "spec": { "host": "${APPLICATION_DOMAIN}", "to": { "kind": "Service", - "name": "django-example" + "name": "${NAME}" } } }, @@ -53,7 +53,7 @@ "kind": "ImageStream", "apiVersion": "v1", "metadata": { - "name": "django-example", + "name": "${NAME}", "annotations": { "description": "Keeps track of changes in the application image" } @@ -63,7 +63,7 @@ "kind": "BuildConfig", "apiVersion": "v1", "metadata": { - "name": "django-example", + "name": "${NAME}", "annotations": { "description": "Defines how to build the application" } @@ -82,7 +82,7 @@ "sourceStrategy": { "from": { "kind": "ImageStreamTag", - "namespace": "openshift", + "namespace": "${NAMESPACE}", "name": "python:3.4" } } @@ -90,7 +90,7 @@ "output": { "to": { "kind": "ImageStreamTag", - "name": "django-example:latest" + "name": "${NAME}:latest" } }, "triggers": [ @@ -116,7 +116,7 @@ "kind": "DeploymentConfig", "apiVersion": "v1", "metadata": { - "name": "django-example", + "name": "${NAME}", "annotations": { "description": "Defines how to deploy the application server" } @@ -135,7 +135,7 @@ ], "from": { "kind": "ImageStreamTag", - "name": "django-example:latest" + "name": "${NAME}:latest" } } }, @@ -145,13 +145,13 @@ ], "replicas": 1, "selector": { - "name": "django-example" + "name": "${NAME}" }, "template": { "metadata": { - "name": "django-example", + "name": "${NAME}", "labels": { - "name": "django-example" + "name": "${NAME}" } }, "spec": { @@ -224,74 +224,83 @@ ], "parameters": [ { + "name": "NAME", + "displayName": "Name", + "description": "The name assigned to all of the frontend objects defined in this template.", + "required": true, + "value": "django-example" + }, + { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { "name": "MEMORY_LIMIT", - "displayName": "Memory limit", - "description": "Maximum amount of memory the container can use", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the container can use.", "value": "512Mi" }, { "name": "SOURCE_REPOSITORY_URL", - "displayName": "Source repository URL", - "description": "The URL of the repository with your application source code", + "displayName": "Git Repository URL", + "description": "The URL of the repository with your application source code.", "value": "https://github.com/openshift/django-ex.git" }, { "name": "SOURCE_REPOSITORY_REF", - "displayName": "Source repository reference", - "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch" + "displayName": "Git Reference", + "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch." }, { "name": "CONTEXT_DIR", - "displayName": "Context directory", - "description": "Set this to the relative path to your project if it is not in the root of your repository" + "displayName": "Context Directory", + "description": "Set this to the relative path to your project if it is not in the root of your repository." }, { "name": "APPLICATION_DOMAIN", - "displayName": "Application hostname", + "displayName": "Application Hostname", "description": "The exposed hostname that will route to the Django service, if left blank a value will be defaulted.", "value": "" }, { "name": "GITHUB_WEBHOOK_SECRET", - "displayName": "GitHub webhook secret", - "description": "A secret string used to configure the GitHub webhook", + "displayName": "GitHub Webhook Secret", + "description": "A secret string used to configure the GitHub webhook.", "generate": "expression", "from": "[a-zA-Z0-9]{40}" }, { "name": "DATABASE_SERVICE_NAME", - "displayName": "Database service name", - "description": "Database service name" + "displayName": "Database Service Name" }, { "name": "DATABASE_ENGINE", - "displayName": "Database engine", - "description": "Database engine: postgresql, mysql or sqlite (default)" + "displayName": "Database Engine", + "description": "Database engine: postgresql, mysql or sqlite (default)." }, { "name": "DATABASE_NAME", - "displayName": "Database name", - "description": "Database name" + "displayName": "Database Name" }, { "name": "DATABASE_USER", - "displayName": "Database user name", - "description": "Database user name" + "displayName": "Database Username" }, { "name": "DATABASE_PASSWORD", - "displayName": "Database user password", - "description": "Database user password" + "displayName": "Database User Password" }, { "name": "APP_CONFIG", - "displayName": "Application configuration file path", - "description": "Relative path to Gunicorn configuration file (optional)" + "displayName": "Application Configuration File Path", + "description": "Relative path to Gunicorn configuration file (optional)." }, { "name": "DJANGO_SECRET_KEY", - "displayName": "Djange secret key", - "description": "Set this to a long random string", + "displayName": "Django Secret Key", + "description": "Set this to a long random string.", "generate": "expression", "from": "[\\w]{50}" } diff --git a/roles/openshift_examples/files/examples/v1.2/quickstart-templates/jenkins-ephemeral-template.json b/roles/openshift_examples/files/examples/v1.2/quickstart-templates/jenkins-ephemeral-template.json index 024d7bfef..e464b5971 100644 --- a/roles/openshift_examples/files/examples/v1.2/quickstart-templates/jenkins-ephemeral-template.json +++ b/roles/openshift_examples/files/examples/v1.2/quickstart-templates/jenkins-ephemeral-template.json @@ -78,7 +78,7 @@ "from": { "kind": "ImageStreamTag", "name": "jenkins:latest", - "namespace": "openshift" + "namespace": "${NAMESPACE}" }, "lastTriggeredImage": "" } @@ -103,6 +103,22 @@ { "name": "jenkins", "image": "${JENKINS_IMAGE}", + "readinessProbe": { + "timeoutSeconds": 3, + "initialDelaySeconds": 3, + "httpGet": { + "path": "/login", + "port": 8080 + } + }, + "livenessProbe": { + "timeoutSeconds": 3, + "initialDelaySeconds": 30, + "httpGet": { + "path": "/login", + "port": 8080 + } + }, "env": [ { "name": "JENKINS_PASSWORD", @@ -147,20 +163,26 @@ "parameters": [ { "name": "MEMORY_LIMIT", - "displayName": "Memory limit", - "description": "Maximum amount of memory the container can use", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the container can use.", "value": "512Mi" }, { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { "name": "JENKINS_SERVICE_NAME", - "displayName": "Jenkins service name", - "description": "The name of the OpenShift Service exposed for the Jenkins container", + "displayName": "Jenkins Service Name", + "description": "The name of the OpenShift Service exposed for the Jenkins container.", "value": "jenkins" }, { "name": "JENKINS_PASSWORD", - "displayName": "Jenkins password", - "description": "Password for the Jenkins user", + "displayName": "Jenkins Password", + "description": "Password for the Jenkins user.", "generate": "expression", "value": "password" } diff --git a/roles/openshift_examples/files/examples/v1.2/quickstart-templates/jenkins-persistent-template.json b/roles/openshift_examples/files/examples/v1.2/quickstart-templates/jenkins-persistent-template.json index 4388350cb..6c143fc70 100644 --- a/roles/openshift_examples/files/examples/v1.2/quickstart-templates/jenkins-persistent-template.json +++ b/roles/openshift_examples/files/examples/v1.2/quickstart-templates/jenkins-persistent-template.json @@ -95,7 +95,7 @@ "from": { "kind": "ImageStreamTag", "name": "jenkins:latest", - "namespace": "openshift" + "namespace": "${NAMESPACE}" }, "lastTriggeredImage": "" } @@ -120,6 +120,22 @@ { "name": "jenkins", "image": "${JENKINS_IMAGE}", + "readinessProbe": { + "timeoutSeconds": 3, + "initialDelaySeconds": 3, + "httpGet": { + "path": "/login", + "port": 8080 + } + }, + "livenessProbe": { + "timeoutSeconds": 3, + "initialDelaySeconds": 30, + "httpGet": { + "path": "/login", + "port": 8080 + } + }, "env": [ { "name": "JENKINS_PASSWORD", @@ -164,27 +180,33 @@ "parameters": [ { "name": "MEMORY_LIMIT", - "displayName": "Memory limit", - "description": "Maximum amount of memory the container can use", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the container can use.", "value": "512Mi" }, { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { "name": "JENKINS_SERVICE_NAME", - "displayName": "Jenkins service name", - "description": "The name of the OpenShift Service exposed for the Jenkins container", + "displayName": "Jenkins Service Name", + "description": "The name of the OpenShift Service exposed for the Jenkins container.", "value": "jenkins" }, { "name": "JENKINS_PASSWORD", - "displayName": "Jenkins password", - "description": "Password for the Jenkins user", + "displayName": "Jenkins Password", + "description": "Password for the Jenkins user.", "generate": "expression", "value": "password" }, { "name": "VOLUME_CAPACITY", - "displayName": "Volume capacity", - "description": "Volume space available for data, e.g. 512Mi, 2Gi", + "displayName": "Volume Capacity", + "description": "Volume space available for data, e.g. 512Mi, 2Gi.", "value": "512Mi", "required": true } diff --git a/roles/openshift_examples/files/examples/v1.2/quickstart-templates/nodejs-mongodb.json b/roles/openshift_examples/files/examples/v1.2/quickstart-templates/nodejs-mongodb.json index d5a1be659..3298ef40c 100644 --- a/roles/openshift_examples/files/examples/v1.2/quickstart-templates/nodejs-mongodb.json +++ b/roles/openshift_examples/files/examples/v1.2/quickstart-templates/nodejs-mongodb.json @@ -17,7 +17,7 @@ "kind": "Service", "apiVersion": "v1", "metadata": { - "name": "nodejs-mongodb-example", + "name": "${NAME}", "annotations": { "description": "Exposes and load balances the application pods" } @@ -31,7 +31,7 @@ } ], "selector": { - "name": "nodejs-mongodb-example" + "name": "${NAME}" } } }, @@ -39,13 +39,13 @@ "kind": "Route", "apiVersion": "v1", "metadata": { - "name": "nodejs-mongodb-example" + "name": "${NAME}" }, "spec": { "host": "${APPLICATION_DOMAIN}", "to": { "kind": "Service", - "name": "nodejs-mongodb-example" + "name": "${NAME}" } } }, @@ -53,7 +53,7 @@ "kind": "ImageStream", "apiVersion": "v1", "metadata": { - "name": "nodejs-mongodb-example", + "name": "${NAME}", "annotations": { "description": "Keeps track of changes in the application image" } @@ -63,7 +63,7 @@ "kind": "BuildConfig", "apiVersion": "v1", "metadata": { - "name": "nodejs-mongodb-example", + "name": "${NAME}", "annotations": { "description": "Defines how to build the application" } @@ -82,7 +82,7 @@ "sourceStrategy": { "from": { "kind": "ImageStreamTag", - "namespace": "openshift", + "namespace": "${NAMESPACE}", "name": "nodejs:0.10" } } @@ -90,7 +90,7 @@ "output": { "to": { "kind": "ImageStreamTag", - "name": "nodejs-mongodb-example:latest" + "name": "${NAME}:latest" } }, "triggers": [ @@ -119,7 +119,7 @@ "kind": "DeploymentConfig", "apiVersion": "v1", "metadata": { - "name": "nodejs-mongodb-example", + "name": "${NAME}", "annotations": { "description": "Defines how to deploy the application server" } @@ -138,7 +138,7 @@ ], "from": { "kind": "ImageStreamTag", - "name": "nodejs-mongodb-example:latest" + "name": "${NAME}:latest" } } }, @@ -148,13 +148,13 @@ ], "replicas": 1, "selector": { - "name": "nodejs-mongodb-example" + "name": "${NAME}" }, "template": { "metadata": { - "name": "nodejs-mongodb-example", + "name": "${NAME}", "labels": { - "name": "nodejs-mongodb-example" + "name": "${NAME}" } }, "spec": { @@ -261,7 +261,7 @@ ], "from": { "kind": "ImageStreamTag", - "namespace": "openshift", + "namespace": "${NAMESPACE}", "name": "mongodb:2.6" } } @@ -337,83 +337,94 @@ ], "parameters": [ { + "name": "NAME", + "displayName": "Name", + "description": "The name assigned to all of the frontend objects defined in this template.", + "required": true, + "value": "nodejs-mongodb-example" + }, + { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { "name": "MEMORY_LIMIT", - "displayName": "Memory limit", - "description": "Maximum amount of memory the Node.js container can use", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the Node.js container can use.", "value": "512Mi" }, { "name": "MEMORY_MONGODB_LIMIT", - "displayName": "Memory limit", - "description": "Maximum amount of memory the MongoDB container can use", + "displayName": "Memory Limit (MongoDB)", + "description": "Maximum amount of memory the MongoDB container can use.", "value": "512Mi" }, { "name": "SOURCE_REPOSITORY_URL", - "displayName": "Source repository URL", - "description": "The URL of the repository with your application source code", + "displayName": "Git Repository URL", + "description": "The URL of the repository with your application source code.", "value": "https://github.com/openshift/nodejs-ex.git" }, { "name": "SOURCE_REPOSITORY_REF", - "displayName": "Source repository reference", - "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch" + "displayName": "Git Reference", + "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch." }, { "name": "CONTEXT_DIR", - "displayName": "Context directory", - "description": "Set this to the relative path to your project if it is not in the root of your repository" + "displayName": "Context Directory", + "description": "Set this to the relative path to your project if it is not in the root of your repository." }, { "name": "APPLICATION_DOMAIN", - "displayName": "Application hostname", + "displayName": "Application Hostname", "description": "The exposed hostname that will route to the Node.js service, if left blank a value will be defaulted.", "value": "" }, { "name": "GITHUB_WEBHOOK_SECRET", - "displayName": "GitHub webhook secret", - "description": "A secret string used to configure the GitHub webhook", + "displayName": "GitHub Webhook Secret", + "description": "A secret string used to configure the GitHub webhook.", "generate": "expression", "from": "[a-zA-Z0-9]{40}" }, { "name": "GENERIC_WEBHOOK_SECRET", - "displayName": "Generic webhook secret", - "description": "A secret string used to configure the Generic webhook", + "displayName": "Generic Webhook Secret", + "description": "A secret string used to configure the Generic webhook.", "generate": "expression", "from": "[a-zA-Z0-9]{40}" }, { "name": "DATABASE_SERVICE_NAME", - "displayName": "Database service name", - "description": "Database service name", + "displayName": "Database Service Name", "value": "mongodb" }, { "name": "DATABASE_USER", - "displayName": "MongoDB user name", - "description": "Username for MongoDB user that will be used for accessing the database", + "displayName": "MongoDB Username", + "description": "Username for MongoDB user that will be used for accessing the database.", "generate": "expression", "from": "user[A-Z0-9]{3}" }, { "name": "DATABASE_PASSWORD", - "displayName": "MongoDB password", - "description": "Password for the MongoDB user", + "displayName": "MongoDB Password", + "description": "Password for the MongoDB user.", "generate": "expression", "from": "[a-zA-Z0-9]{16}" }, { "name": "DATABASE_NAME", - "displayName": "Database name", - "description": "Database name", + "displayName": "Database Name", "value": "sampledb" }, { "name": "DATABASE_ADMIN_PASSWORD", - "displayName": "Database administrator password", - "description": "Password for the database admin user", + "displayName": "Database Administrator Password", + "description": "Password for the database admin user.", "generate": "expression", "from": "[a-zA-Z0-9]{16}" } diff --git a/roles/openshift_examples/files/examples/v1.2/quickstart-templates/nodejs.json b/roles/openshift_examples/files/examples/v1.2/quickstart-templates/nodejs.json index ef36a88bd..82df67c4e 100644 --- a/roles/openshift_examples/files/examples/v1.2/quickstart-templates/nodejs.json +++ b/roles/openshift_examples/files/examples/v1.2/quickstart-templates/nodejs.json @@ -17,7 +17,7 @@ "kind": "Service", "apiVersion": "v1", "metadata": { - "name": "nodejs-example", + "name": "${NAME}", "annotations": { "description": "Exposes and load balances the application pods" } @@ -31,7 +31,7 @@ } ], "selector": { - "name": "nodejs-example" + "name": "${NAME}" } } }, @@ -39,13 +39,13 @@ "kind": "Route", "apiVersion": "v1", "metadata": { - "name": "nodejs-example" + "name": "${NAME}" }, "spec": { "host": "${APPLICATION_DOMAIN}", "to": { "kind": "Service", - "name": "nodejs-example" + "name": "${NAME}" } } }, @@ -53,7 +53,7 @@ "kind": "ImageStream", "apiVersion": "v1", "metadata": { - "name": "nodejs-example", + "name": "${NAME}", "annotations": { "description": "Keeps track of changes in the application image" } @@ -63,7 +63,7 @@ "kind": "BuildConfig", "apiVersion": "v1", "metadata": { - "name": "nodejs-example", + "name": "${NAME}", "annotations": { "description": "Defines how to build the application" } @@ -82,7 +82,7 @@ "sourceStrategy": { "from": { "kind": "ImageStreamTag", - "namespace": "openshift", + "namespace": "${NAMESPACE}", "name": "nodejs:0.10" } } @@ -90,7 +90,7 @@ "output": { "to": { "kind": "ImageStreamTag", - "name": "nodejs-example:latest" + "name": "${NAME}:latest" } }, "triggers": [ @@ -119,7 +119,7 @@ "kind": "DeploymentConfig", "apiVersion": "v1", "metadata": { - "name": "nodejs-example", + "name": "${NAME}", "annotations": { "description": "Defines how to deploy the application server" } @@ -138,7 +138,7 @@ ], "from": { "kind": "ImageStreamTag", - "name": "nodejs-example:latest" + "name": "${NAME}:latest" } } }, @@ -148,13 +148,13 @@ ], "replicas": 1, "selector": { - "name": "nodejs-example" + "name": "${NAME}" }, "template": { "metadata": { - "name": "nodejs-example", + "name": "${NAME}", "labels": { - "name": "nodejs-example" + "name": "${NAME}" } }, "spec": { @@ -224,71 +224,82 @@ ], "parameters": [ { + "name": "NAME", + "displayName": "Name", + "description": "The name assigned to all of the frontend objects defined in this template.", + "required": true, + "value": "nodejs-example" + }, + { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { "name": "MEMORY_LIMIT", - "displayName": "Memory limit", - "description": "Maximum amount of memory the container can use", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the container can use.", "value": "512Mi" }, { "name": "SOURCE_REPOSITORY_URL", - "displayName": "Source repository URL", - "description": "The URL of the repository with your application source code", + "displayName": "Git Repository URL", + "description": "The URL of the repository with your application source code.", "value": "https://github.com/openshift/nodejs-ex.git" }, { "name": "SOURCE_REPOSITORY_REF", - "displayName": "Source repository reference", - "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch" + "displayName": "Git Reference", + "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch." }, { "name": "CONTEXT_DIR", - "displayName": "Context directory", - "description": "Set this to the relative path to your project if it is not in the root of your repository" + "displayName": "Context Directory", + "description": "Set this to the relative path to your project if it is not in the root of your repository." }, { "name": "APPLICATION_DOMAIN", - "displayName": "Application hostname", + "displayName": "Application Hostname", "description": "The exposed hostname that will route to the Node.js service, if left blank a value will be defaulted.", "value": "" }, { "name": "GITHUB_WEBHOOK_SECRET", - "displayName": "GitHub webhook secret", - "description": "A secret string used to configure the GitHub webhook", + "displayName": "GitHub Webhook Secret", + "description": "A secret string used to configure the GitHub webhook.", "generate": "expression", "from": "[a-zA-Z0-9]{40}" }, { "name": "GENERIC_WEBHOOK_SECRET", - "displayName": "Generic webhook secret", - "description": "A secret string used to configure the Generic webhook", + "displayName": "Generic Webhook Secret", + "description": "A secret string used to configure the Generic webhook.", "generate": "expression", "from": "[a-zA-Z0-9]{40}" }, { "name": "DATABASE_SERVICE_NAME", - "displayName": "Database service name", - "description": "Database service name" + "displayName": "Database Service Name" }, { "name": "MONGODB_USER", - "displayName": "MongoDB user name", - "description": "Username for MongoDB user that will be used for accessing the database" + "displayName": "MongoDB Username", + "description": "Username for MongoDB user that will be used for accessing the database." }, { "name": "MONGODB_PASSWORD", - "displayName": "MongoDB password", - "description": "Password for the MongoDB user" + "displayName": "MongoDB Password", + "description": "Password for the MongoDB user." }, { "name": "MONGODB_DATABASE", - "displayName": "Database name", - "description": "Database name" + "displayName": "Database Name" }, { "name": "MONGODB_ADMIN_PASSWORD", - "displayName": "Database administrator password", - "description": "Password for the database admin user" + "displayName": "Database Administrator Password", + "description": "Password for the database admin user." } ] } diff --git a/roles/openshift_examples/files/examples/v1.2/quickstart-templates/rails-postgresql.json b/roles/openshift_examples/files/examples/v1.2/quickstart-templates/rails-postgresql.json index 7599f70aa..6292cf3e7 100644 --- a/roles/openshift_examples/files/examples/v1.2/quickstart-templates/rails-postgresql.json +++ b/roles/openshift_examples/files/examples/v1.2/quickstart-templates/rails-postgresql.json @@ -17,7 +17,7 @@ "kind": "Service", "apiVersion": "v1", "metadata": { - "name": "rails-postgresql-example", + "name": "${NAME}", "annotations": { "description": "Exposes and load balances the application pods" } @@ -31,7 +31,7 @@ } ], "selector": { - "name": "rails-postgresql-example" + "name": "${NAME}" } } }, @@ -39,13 +39,13 @@ "kind": "Route", "apiVersion": "v1", "metadata": { - "name": "rails-postgresql-example" + "name": "${NAME}" }, "spec": { "host": "${APPLICATION_DOMAIN}", "to": { "kind": "Service", - "name": "rails-postgresql-example" + "name": "${NAME}" } } }, @@ -53,7 +53,7 @@ "kind": "ImageStream", "apiVersion": "v1", "metadata": { - "name": "rails-postgresql-example", + "name": "${NAME}", "annotations": { "description": "Keeps track of changes in the application image" } @@ -63,7 +63,7 @@ "kind": "BuildConfig", "apiVersion": "v1", "metadata": { - "name": "rails-postgresql-example", + "name": "${NAME}", "annotations": { "description": "Defines how to build the application" } @@ -82,7 +82,7 @@ "sourceStrategy": { "from": { "kind": "ImageStreamTag", - "namespace": "openshift", + "namespace": "${NAMESPACE}", "name": "ruby:2.2" } } @@ -90,7 +90,7 @@ "output": { "to": { "kind": "ImageStreamTag", - "name": "rails-postgresql-example:latest" + "name": "${NAME}:latest" } }, "triggers": [ @@ -116,7 +116,7 @@ "kind": "DeploymentConfig", "apiVersion": "v1", "metadata": { - "name": "rails-postgresql-example", + "name": "${NAME}", "annotations": { "description": "Defines how to deploy the application server" } @@ -131,7 +131,7 @@ "command": [ "./migrate-database.sh" ], - "containerName": "rails-postgresql-example" + "containerName": "${NAME}" } } } @@ -146,7 +146,7 @@ ], "from": { "kind": "ImageStreamTag", - "name": "rails-postgresql-example:latest" + "name": "${NAME}:latest" } } }, @@ -156,13 +156,13 @@ ], "replicas": 1, "selector": { - "name": "rails-postgresql-example" + "name": "${NAME}" }, "template": { "metadata": { - "name": "rails-postgresql-example", + "name": "${NAME}", "labels": { - "name": "rails-postgresql-example" + "name": "${NAME}" } }, "spec": { @@ -293,7 +293,7 @@ ], "from": { "kind": "ImageStreamTag", - "namespace": "openshift", + "namespace": "${NAMESPACE}", "name": "postgresql:9.4" } } @@ -373,107 +373,114 @@ ], "parameters": [ { + "name": "NAME", + "displayName": "Name", + "description": "The name assigned to all of the frontend objects defined in this template.", + "required": true, + "value": "rails-postgresql-example" + }, + { + "name": "NAMESPACE", + "displayName": "Namespace", + "description": "The OpenShift Namespace where the ImageStream resides.", + "value": "openshift" + }, + { "name": "MEMORY_LIMIT", - "displayName": "Memory limit", - "description": "Maximum amount of memory the Rails container can use", + "displayName": "Memory Limit", + "description": "Maximum amount of memory the Rails container can use.", "value": "512Mi" }, { "name": "MEMORY_POSTGRESQL_LIMIT", - "displayName": "Memory limit", - "description": "Maximum amount of memory the PostgreSQL container can use", + "displayName": "Memory Limit (PostgreSQL)", + "description": "Maximum amount of memory the PostgreSQL container can use.", "value": "512Mi" }, { "name": "SOURCE_REPOSITORY_URL", - "displayName": "Source repository URL", - "description": "The URL of the repository with your application source code", + "displayName": "Git Repository URL", + "description": "The URL of the repository with your application source code.", "value": "https://github.com/openshift/rails-ex.git" }, { "name": "SOURCE_REPOSITORY_REF", - "displayName": "Source repository reference", - "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch" + "displayName": "Git Reference", + "description": "Set this to a branch name, tag or other ref of your repository if you are not using the default branch." }, { "name": "CONTEXT_DIR", - "displayName": "Context directory", - "description": "Set this to the relative path to your project if it is not in the root of your repository" + "displayName": "Context Directory", + "description": "Set this to the relative path to your project if it is not in the root of your repository." }, { "name": "APPLICATION_DOMAIN", - "displayName": "Application hostname", + "displayName": "Application Hostname", "description": "The exposed hostname that will route to the Rails service, if left blank a value will be defaulted.", "value": "" }, { "name": "GITHUB_WEBHOOK_SECRET", - "displayName": "GitHub webhook secret", - "description": "A secret string used to configure the GitHub webhook", + "displayName": "GitHub Webhook Secret", + "description": "A secret string used to configure the GitHub webhook.", "generate": "expression", "from": "[a-zA-Z0-9]{40}" }, { "name": "SECRET_KEY_BASE", - "displayName": "Secret key", - "description": "Your secret key for verifying the integrity of signed cookies", + "displayName": "Secret Key", + "description": "Your secret key for verifying the integrity of signed cookies.", "generate": "expression", "from": "[a-z0-9]{127}" }, { "name": "APPLICATION_USER", - "displayName": "Application user name", - "description": "The application user that is used within the sample application to authorize access on pages", + "displayName": "Application Username", + "description": "The application user that is used within the sample application to authorize access on pages.", "value": "openshift" }, { "name": "APPLICATION_PASSWORD", - "displayName": "Application password", - "description": "The application password that is used within the sample application to authorize access on pages", + "displayName": "Application Password", + "description": "The application password that is used within the sample application to authorize access on pages.", "value": "secret" }, { "name": "RAILS_ENV", - "displayName": "Rails environment", - "description": "Environment under which the sample application will run. Could be set to production, development or test", + "displayName": "Rails Environment", + "description": "Environment under which the sample application will run. Could be set to production, development or test.", "value": "production" }, { "name": "DATABASE_SERVICE_NAME", - "displayName": "Database service name", - "description": "Database service name", + "displayName": "Database Service Name", "value": "postgresql" }, { "name": "DATABASE_USER", - "displayName": "Database user name", - "description": "database username", + "displayName": "Database Username", "generate": "expression", "from": "user[A-Z0-9]{3}" }, { "name": "DATABASE_PASSWORD", - "displayName": "Database password", - "description": "database password", + "displayName": "Database Password", "generate": "expression", "from": "[a-zA-Z0-9]{8}" }, { "name": "DATABASE_NAME", - "displayName": "Database name", - "description": "database name", + "displayName": "Database Name", "value": "root" }, { "name": "POSTGRESQL_MAX_CONNECTIONS", - "displayName": "Maximum database connections", - "description": "database max connections", + "displayName": "Maximum Database Connections", "value": "100" }, { "name": "POSTGRESQL_SHARED_BUFFERS", - "displayName": "Shared buffer amount", - "description": "database shared buffers", + "displayName": "Shared Buffer Amount", "value": "12MB" } ] diff --git a/roles/openshift_facts/library/openshift_facts.py b/roles/openshift_facts/library/openshift_facts.py index 596e4f894..0d31d4ddf 100755 --- a/roles/openshift_facts/library/openshift_facts.py +++ b/roles/openshift_facts/library/openshift_facts.py @@ -26,6 +26,44 @@ from distutils.util import strtobool from distutils.version import LooseVersion import struct import socket +from dbus import SystemBus, Interface +from dbus.exceptions import DBusException + + +def migrate_docker_facts(facts): + """ Apply migrations for docker facts """ + params = { + 'common': ( + 'additional_registries', + 'insecure_registries', + 'blocked_registries', + 'options' + ), + 'node': ( + 'log_driver', + 'log_options' + ) + } + if 'docker' not in facts: + facts['docker'] = {} + for role in params.keys(): + if role in facts: + for param in params[role]: + old_param = 'docker_' + param + if old_param in facts[role]: + facts['docker'][param] = facts[role].pop(old_param) + + if 'node' in facts and 'portal_net' in facts['node']: + facts['docker']['hosted_registry_insecure'] = True + facts['docker']['hosted_registry_network'] = facts['node'].pop('portal_net') + return facts + +def migrate_local_facts(facts): + """ Apply migrations of local facts """ + migrated_facts = copy.deepcopy(facts) + return migrate_docker_facts(migrated_facts) + + def first_ip(network): """ Return the first IPv4 address in network @@ -296,9 +334,10 @@ def normalize_provider_facts(provider, metadata): facts = dict(name=provider, metadata=metadata, network=dict(interfaces=[], ipv6_enabled=False)) + if provider == 'gce': facts = normalize_gce_facts(metadata, facts) - elif provider == 'ec2': + elif provider == 'aws': facts = normalize_aws_facts(metadata, facts) elif provider == 'openstack': facts = normalize_openstack_facts(metadata, facts) @@ -657,18 +696,13 @@ def set_deployment_facts_if_unset(facts): data_dir = '/var/lib/openshift' facts['common']['data_dir'] = data_dir - # remove duplicate and empty strings from registry lists - for cat in ['additional', 'blocked', 'insecure']: - key = 'docker_{0}_registries'.format(cat) - if key in facts['common']: - facts['common'][key] = list(set(facts['common'][key]) - set([''])) - - + if 'docker' in facts: + deployment_type = facts['common']['deployment_type'] if deployment_type in ['enterprise', 'atomic-enterprise', 'openshift-enterprise']: - addtl_regs = facts['common'].get('docker_additional_registries', []) + addtl_regs = facts['docker'].get('additional_registries', []) ent_reg = 'registry.access.redhat.com' if ent_reg not in addtl_regs: - facts['common']['docker_additional_registries'] = addtl_regs + [ent_reg] + facts['docker']['additional_registries'] = addtl_regs + [ent_reg] for role in ('master', 'node'): if role in facts: @@ -713,8 +747,9 @@ def set_version_facts_if_unset(facts): """ if 'common' in facts: deployment_type = facts['common']['deployment_type'] - facts['common']['version'] = version = get_openshift_version(facts) + version = get_openshift_version(facts) if version is not None: + facts['common']['version'] = version if deployment_type == 'origin': version_gte_3_1_or_1_1 = LooseVersion(version) >= LooseVersion('1.1.0') version_gte_3_1_1_or_1_1_1 = LooseVersion(version) >= LooseVersion('1.1.1') @@ -775,7 +810,7 @@ def set_sdn_facts_if_unset(facts, system_facts): if 'common' in facts: use_sdn = facts['common']['use_openshift_sdn'] if not (use_sdn == '' or isinstance(use_sdn, bool)): - use_sdn = bool(strtobool(str(use_sdn))) + use_sdn = safe_get_bool(use_sdn) facts['common']['use_openshift_sdn'] = use_sdn if 'sdn_network_plugin_name' not in facts['common']: plugin = 'redhat/openshift-ovs-subnet' if use_sdn else '' @@ -882,6 +917,101 @@ def get_current_config(facts): return current_config +def build_kubelet_args(facts): + """ Build node kubelet_args """ + cloud_cfg_path = os.path.join(facts['common']['config_base'], + 'cloudprovider') + if 'node' in facts: + kubelet_args = {} + if 'cloudprovider' in facts: + if facts['cloudprovider']['kind'] == 'aws': + kubelet_args['cloud-provider'] = ['aws'] + kubelet_args['cloud-config'] = [cloud_cfg_path + '/aws.conf'] + if facts['cloudprovider']['kind'] == 'openstack': + kubelet_args['cloud-provider'] = ['openstack'] + kubelet_args['cloud-config'] = [cloud_cfg_path + '/openstack.conf'] + if kubelet_args != {}: + facts = merge_facts({'node': {'kubelet_args': kubelet_args}}, facts, [], []) + return facts + +def build_controller_args(facts): + """ Build master controller_args """ + cloud_cfg_path = os.path.join(facts['common']['config_base'], + 'cloudprovider') + if 'master' in facts: + controller_args = {} + if 'cloudprovider' in facts: + if facts['cloudprovider']['kind'] == 'aws': + controller_args['cloud-provider'] = ['aws'] + controller_args['cloud-config'] = [cloud_cfg_path + '/aws.conf'] + if facts['cloudprovider']['kind'] == 'openstack': + controller_args['cloud-provider'] = ['openstack'] + controller_args['cloud-config'] = [cloud_cfg_path + '/openstack.conf'] + if controller_args != {}: + facts = merge_facts({'master': {'controller_args': controller_args}}, facts, [], []) + return facts + +def build_api_server_args(facts): + """ Build master api_server_args """ + cloud_cfg_path = os.path.join(facts['common']['config_base'], + 'cloudprovider') + if 'master' in facts: + api_server_args = {} + if 'cloudprovider' in facts: + if facts['cloudprovider']['kind'] == 'aws': + api_server_args['cloud-provider'] = ['aws'] + api_server_args['cloud-config'] = [cloud_cfg_path + '/aws.conf'] + if facts['cloudprovider']['kind'] == 'openstack': + api_server_args['cloud-provider'] = ['openstack'] + api_server_args['cloud-config'] = [cloud_cfg_path + '/openstack.conf'] + if api_server_args != {}: + facts = merge_facts({'master': {'api_server_args': api_server_args}}, facts, [], []) + return facts + +def is_service_running(service): + """ Queries systemd through dbus to see if the service is running """ + service_running = False + bus = SystemBus() + systemd = bus.get_object('org.freedesktop.systemd1', '/org/freedesktop/systemd1') + manager = Interface(systemd, dbus_interface='org.freedesktop.systemd1.Manager') + try: + service_unit = service if service.endswith('.service') else manager.GetUnit('{0}.service'.format(service)) + service_proxy = bus.get_object('org.freedesktop.systemd1', str(service_unit)) + service_properties = Interface(service_proxy, dbus_interface='org.freedesktop.DBus.Properties') + service_load_state = service_properties.Get('org.freedesktop.systemd1.Unit', 'LoadState') + service_active_state = service_properties.Get('org.freedesktop.systemd1.Unit', 'ActiveState') + if service_load_state == 'loaded' and service_active_state == 'active': + service_running = True + except DBusException: + pass + + return service_running + +def get_version_output(binary, version_cmd): + """ runs and returns the version output for a command """ + cmd = [] + for item in (binary, version_cmd): + if isinstance(item, list): + cmd.extend(item) + else: + cmd.append(item) + + if os.path.isfile(cmd[0]): + _, output, _ = module.run_command(cmd) + return output + +def get_docker_version_info(): + """ Parses and returns the docker version info """ + result = None + if is_service_running('docker'): + version_info = yaml.safe_load(get_version_output('/usr/bin/docker', 'version')) + if 'Server' in version_info: + result = { + 'api_version': version_info['Server']['API version'], + 'version': version_info['Server']['Version'] + } + return result + def get_openshift_version(facts, cli_image=None): """ Get current version of openshift on the host @@ -904,7 +1034,7 @@ def get_openshift_version(facts, cli_image=None): _, output, _ = module.run_command(['/usr/bin/openshift', 'version']) version = parse_openshift_version(output) - if 'is_containerized' in facts['common'] and facts['common']['is_containerized']: + if 'is_containerized' in facts['common'] and safe_get_bool(facts['common']['is_containerized']): container = None if 'master' in facts: if 'cluster_method' in facts['master']: @@ -923,9 +1053,10 @@ def get_openshift_version(facts, cli_image=None): if version is None and cli_image is not None: # Assume we haven't installed the environment yet and we need - # to query the latest image - exit_code, output, _ = module.run_command(['docker', 'run', '--rm', cli_image, 'version']) - version = parse_openshift_version(output) + # to query the latest image, but only if docker is installed + if 'docker' in facts and 'version' in facts['docker']: + exit_code, output, _ = module.run_command(['docker', 'run', '--rm', cli_image, 'version']) + version = parse_openshift_version(output) return version @@ -1032,7 +1163,7 @@ def merge_facts(orig, new, additive_facts_to_overwrite, protected_facts_to_overw # ha (bool) can not change unless it has been passed # as a protected fact to overwrite. if key == 'ha': - if bool(value) != bool(new[key]): + if safe_get_bool(value) != safe_get_bool(new[key]): module.fail_json(msg='openshift_facts received a different value for openshift.master.ha') else: facts[key] = value @@ -1048,7 +1179,6 @@ def merge_facts(orig, new, additive_facts_to_overwrite, protected_facts_to_overw facts[key] = copy.deepcopy(new[key]) return facts - def save_local_facts(filename, facts): """ Save local facts @@ -1097,7 +1227,17 @@ def get_local_facts_from_file(filename): return local_facts +def safe_get_bool(fact): + """ Get a boolean fact safely. + + Args: + facts: fact to convert + Returns: + bool: given fact as a bool + """ + return bool(strtobool(str(fact))) +# pylint: disable=too-many-statements def set_container_facts_if_unset(facts): """ Set containerized facts. @@ -1114,24 +1254,44 @@ def set_container_facts_if_unset(facts): node_image = 'openshift3/node' ovs_image = 'openshift3/openvswitch' etcd_image = 'registry.access.redhat.com/rhel7/etcd' + pod_image = 'openshift3/ose-pod' + router_image = 'openshift3/ose-haproxy-router' + registry_image = 'openshift3/ose-docker-registry' + deployer_image = 'openshift3/ose-deployer' elif deployment_type == 'atomic-enterprise': master_image = 'aep3_beta/aep' cli_image = master_image node_image = 'aep3_beta/node' ovs_image = 'aep3_beta/openvswitch' etcd_image = 'registry.access.redhat.com/rhel7/etcd' + pod_image = 'aep3_beta/aep-pod' + router_image = 'aep3_beta/aep-haproxy-router' + registry_image = 'aep3_beta/aep-docker-registry' + deployer_image = 'aep3_beta/aep-deployer' else: master_image = 'openshift/origin' cli_image = master_image node_image = 'openshift/node' ovs_image = 'openshift/openvswitch' etcd_image = 'registry.access.redhat.com/rhel7/etcd' + pod_image = 'openshift/origin-pod' + router_image = 'openshift/origin-haproxy-router' + registry_image = 'openshift/origin-docker-registry' + deployer_image = 'openshift/origin-deployer' facts['common']['is_atomic'] = os.path.isfile('/run/ostree-booted') if 'is_containerized' not in facts['common']: facts['common']['is_containerized'] = facts['common']['is_atomic'] if 'cli_image' not in facts['common']: facts['common']['cli_image'] = cli_image + if 'pod_image' not in facts['common']: + facts['common']['pod_image'] = pod_image + if 'router_image' not in facts['common']: + facts['common']['router_image'] = router_image + if 'registry_image' not in facts['common']: + facts['common']['registry_image'] = registry_image + if 'deployer_image' not in facts['common']: + facts['common']['deployer_image'] = deployer_image if 'etcd' in facts and 'etcd_image' not in facts['etcd']: facts['etcd']['etcd_image'] = etcd_image if 'master' in facts and 'master_image' not in facts['master']: @@ -1142,11 +1302,13 @@ def set_container_facts_if_unset(facts): if 'ovs_image' not in facts['node']: facts['node']['ovs_image'] = ovs_image - if bool(strtobool(str(facts['common']['is_containerized']))): + if safe_get_bool(facts['common']['is_containerized']): facts['common']['admin_binary'] = '/usr/local/bin/oadm' facts['common']['client_binary'] = '/usr/local/bin/oc' - base_version = get_openshift_version(facts, cli_image).split('-')[0] - facts['common']['image_tag'] = "v" + base_version + openshift_version = get_openshift_version(facts, cli_image) + if openshift_version is not None: + base_version = openshift_version.split('-')[0] + facts['common']['image_tag'] = "v" + base_version return facts @@ -1212,13 +1374,20 @@ class OpenShiftFacts(object): Raises: OpenShiftFactsUnsupportedRoleError: """ - known_roles = ['common', 'master', 'node', 'etcd', 'hosted'] + known_roles = ['cloudprovider', + 'common', + 'docker', + 'etcd', + 'hosted', + 'master', + 'node'] # Disabling too-many-arguments, this should be cleaned up as a TODO item. # pylint: disable=too-many-arguments def __init__(self, role, filename, local_facts, additive_facts_to_overwrite=None, openshift_env=None, + openshift_env_structures=None, protected_facts_to_overwrite=None): self.changed = False self.filename = filename @@ -1231,12 +1400,14 @@ class OpenShiftFacts(object): self.facts = self.generate_facts(local_facts, additive_facts_to_overwrite, openshift_env, + openshift_env_structures, protected_facts_to_overwrite) def generate_facts(self, local_facts, additive_facts_to_overwrite, openshift_env, + openshift_env_structures, protected_facts_to_overwrite): """ Generate facts @@ -1253,10 +1424,17 @@ class OpenShiftFacts(object): local_facts = self.init_local_facts(local_facts, additive_facts_to_overwrite, openshift_env, + openshift_env_structures, protected_facts_to_overwrite) roles = local_facts.keys() - defaults = self.get_defaults(roles) + + if 'common' in local_facts and 'deployment_type' in local_facts['common']: + deployment_type = local_facts['common']['deployment_type'] + else: + deployment_type = 'origin' + + defaults = self.get_defaults(roles, deployment_type) provider_facts = self.init_provider_facts() facts = apply_provider_facts(defaults, provider_facts) facts = merge_facts(facts, @@ -1274,16 +1452,19 @@ class OpenShiftFacts(object): facts = set_identity_providers_if_unset(facts) facts = set_sdn_facts_if_unset(facts, self.system_facts) facts = set_deployment_facts_if_unset(facts) + facts = set_container_facts_if_unset(facts) + facts = build_kubelet_args(facts) + facts = build_controller_args(facts) + facts = build_api_server_args(facts) facts = set_version_facts_if_unset(facts) facts = set_manageiq_facts_if_unset(facts) facts = set_aggregate_facts(facts) facts = set_etcd_facts_if_unset(facts) - facts = set_container_facts_if_unset(facts) - if not facts['common']['is_containerized']: + if not safe_get_bool(facts['common']['is_containerized']): facts = set_installed_variant_rpm_facts(facts) return dict(openshift=facts) - def get_defaults(self, roles): + def get_defaults(self, roles, deployment_type): """ Get default fact values Args: @@ -1292,8 +1473,7 @@ class OpenShiftFacts(object): Returns: dict: The generated default facts """ - defaults = dict() - + defaults = {} ip_addr = self.system_facts['default_ipv4']['address'] exit_code, output, _ = module.run_command(['hostname', '-f']) hostname_f = output.strip() if exit_code == 0 else '' @@ -1301,33 +1481,65 @@ class OpenShiftFacts(object): self.system_facts['fqdn']] hostname = choose_hostname(hostname_values, ip_addr) - common = dict(use_openshift_sdn=True, ip=ip_addr, public_ip=ip_addr, - deployment_type='origin', hostname=hostname, - public_hostname=hostname) - common['client_binary'] = 'oc' - common['admin_binary'] = 'oadm' - common['dns_domain'] = 'cluster.local' - common['install_examples'] = True - defaults['common'] = common + defaults['common'] = dict(use_openshift_sdn=True, ip=ip_addr, + public_ip=ip_addr, + deployment_type=deployment_type, + hostname=hostname, + public_hostname=hostname, + client_binary='oc', admin_binary='oadm', + dns_domain='cluster.local', + install_examples=True, + debug_level=2) if 'master' in roles: - master = dict(api_use_ssl=True, api_port='8443', controllers_port='8444', - console_use_ssl=True, console_path='/console', - console_port='8443', etcd_use_ssl=True, etcd_hosts='', - etcd_port='4001', portal_net='172.30.0.0/16', - embedded_etcd=True, embedded_kube=True, - embedded_dns=True, dns_port='53', - bind_addr='0.0.0.0', session_max_seconds=3600, - session_name='ssn', session_secrets_file='', - access_token_max_seconds=86400, - auth_token_max_seconds=500, - oauth_grant_method='auto') - defaults['master'] = master + scheduler_predicates = [ + {"name": "MatchNodeSelector"}, + {"name": "PodFitsResources"}, + {"name": "PodFitsPorts"}, + {"name": "NoDiskConflict"}, + {"name": "Region", "argument": {"serviceAffinity" : {"labels" : ["region"]}}} + ] + scheduler_priorities = [ + {"name": "LeastRequestedPriority", "weight": 1}, + {"name": "SelectorSpreadPriority", "weight": 1}, + {"name": "Zone", "weight" : 2, "argument": {"serviceAntiAffinity" : {"label": "zone"}}} + ] + + defaults['master'] = dict(api_use_ssl=True, api_port='8443', + controllers_port='8444', + console_use_ssl=True, + console_path='/console', + console_port='8443', etcd_use_ssl=True, + etcd_hosts='', etcd_port='4001', + portal_net='172.30.0.0/16', + embedded_etcd=True, embedded_kube=True, + embedded_dns=True, dns_port='53', + bind_addr='0.0.0.0', + session_max_seconds=3600, + session_name='ssn', + session_secrets_file='', + access_token_max_seconds=86400, + auth_token_max_seconds=500, + oauth_grant_method='auto', + scheduler_predicates=scheduler_predicates, + scheduler_priorities=scheduler_priorities) if 'node' in roles: - node = dict(labels={}, annotations={}, portal_net='172.30.0.0/16', - iptables_sync_period='5s', set_node_ip=False) - defaults['node'] = node + defaults['node'] = dict(labels={}, annotations={}, + iptables_sync_period='5s', + local_quota_per_fsgroup="", + set_node_ip=False) + + if 'docker' in roles: + docker = dict(disable_push_dockerhub=False) + version_info = get_docker_version_info() + if version_info is not None: + docker['api_version'] = version_info['api_version'] + docker['version'] = version_info['version'] + defaults['docker'] = docker + + if 'cloudprovider' in roles: + defaults['cloudprovider'] = dict(kind=None) defaults['hosted'] = dict( registry=dict( @@ -1382,7 +1594,7 @@ class OpenShiftFacts(object): metadata['instance'].pop('serviceAccounts', None) elif (virt_type == 'xen' and virt_role == 'guest' and re.match(r'.*\.amazon$', product_version)): - provider = 'ec2' + provider = 'aws' metadata_url = 'http://169.254.169.254/latest/meta-data/' metadata = get_provider_metadata(metadata_url) elif re.search(r'OpenStack', product_name): @@ -1424,13 +1636,55 @@ class OpenShiftFacts(object): ) return provider_facts - # Disabling too-many-branches. This should be cleaned up as a TODO item. - #pylint: disable=too-many-branches + @staticmethod + def split_openshift_env_fact_keys(openshift_env_fact, openshift_env_structures): + """ Split openshift_env facts based on openshift_env structures. + + Args: + openshift_env_fact (string): the openshift_env fact to split + ex: 'openshift_cloudprovider_openstack_auth_url' + openshift_env_structures (list): a list of structures to determine fact keys + ex: ['openshift.cloudprovider.openstack.*'] + Returns: + list: a list of keys that represent the fact + ex: ['openshift', 'cloudprovider', 'openstack', 'auth_url'] + """ + # By default, we'll split an openshift_env fact by underscores. + fact_keys = openshift_env_fact.split('_') + + # Determine if any of the provided variable structures match the fact. + matching_structure = None + if openshift_env_structures != None: + for structure in openshift_env_structures: + if re.match(structure, openshift_env_fact): + matching_structure = structure + # Fact didn't match any variable structures so return the default fact keys. + if matching_structure is None: + return fact_keys + + final_keys = [] + structure_keys = matching_structure.split('.') + for structure_key in structure_keys: + # Matched current key. Add to final keys. + if structure_key == fact_keys[structure_keys.index(structure_key)]: + final_keys.append(structure_key) + # Wildcard means we will be taking everything from here to the end of the fact. + elif structure_key == '*': + final_keys.append('_'.join(fact_keys[structure_keys.index(structure_key):])) + # Shouldn't have gotten here, return the fact keys. + else: + return fact_keys + return final_keys + + # Disabling too-many-branches and too-many-locals. + # This should be cleaned up as a TODO item. + #pylint: disable=too-many-branches, too-many-locals def init_local_facts(self, facts=None, additive_facts_to_overwrite=None, openshift_env=None, + openshift_env_structures=None, protected_facts_to_overwrite=None): - """ Initialize the provider facts + """ Initialize the local facts Args: facts (dict): local facts to set @@ -1456,8 +1710,8 @@ class OpenShiftFacts(object): for fact, value in openshift_env.iteritems(): oo_env_facts = dict() current_level = oo_env_facts - keys = fact.split('_')[1:] - if keys[0] != self.role: + keys = self.split_openshift_env_fact_keys(fact, openshift_env_structures)[1:] + if len(keys) > 0 and keys[0] != self.role: continue for key in keys: if key == keys[-1]: @@ -1472,15 +1726,23 @@ class OpenShiftFacts(object): local_facts = get_local_facts_from_file(self.filename) - for arg in ['labels', 'annotations']: - if arg in facts_to_set and isinstance(facts_to_set[arg], - basestring): - facts_to_set[arg] = module.from_json(facts_to_set[arg]) + migrated_facts = migrate_local_facts(local_facts) - new_local_facts = merge_facts(local_facts, + new_local_facts = merge_facts(migrated_facts, facts_to_set, additive_facts_to_overwrite, protected_facts_to_overwrite) + + if 'docker' in new_local_facts: + # remove duplicate and empty strings from registry lists + for cat in ['additional', 'blocked', 'insecure']: + key = '{0}_registries'.format(cat) + if key in new_local_facts['docker']: + val = new_local_facts['docker'][key] + if isinstance(val, basestring): + val = [x.strip() for x in val.split(',')] + new_local_facts['docker'][key] = list(set(val) - set([''])) + for facts in new_local_facts.values(): keys_to_delete = [] if isinstance(facts, dict): @@ -1577,6 +1839,7 @@ def main(): local_facts=dict(default=None, type='dict', required=False), additive_facts_to_overwrite=dict(default=[], type='list', required=False), openshift_env=dict(default={}, type='dict', required=False), + openshift_env_structures=dict(default=[], type='list', required=False), protected_facts_to_overwrite=dict(default=[], type='list', required=False), ), supports_check_mode=True, @@ -1587,6 +1850,7 @@ def main(): local_facts = module.params['local_facts'] additive_facts_to_overwrite = module.params['additive_facts_to_overwrite'] openshift_env = module.params['openshift_env'] + openshift_env_structures = module.params['openshift_env_structures'] protected_facts_to_overwrite = module.params['protected_facts_to_overwrite'] fact_file = '/etc/ansible/facts.d/openshift.fact' @@ -1596,6 +1860,7 @@ def main(): local_facts, additive_facts_to_overwrite, openshift_env, + openshift_env_structures, protected_facts_to_overwrite) file_params = module.params.copy() diff --git a/roles/openshift_facts/tasks/main.yml b/roles/openshift_facts/tasks/main.yml index 0dbac1b54..36def57c8 100644 --- a/roles/openshift_facts/tasks/main.yml +++ b/roles/openshift_facts/tasks/main.yml @@ -15,7 +15,7 @@ - set_fact: l_is_atomic: "{{ ostree_output.rc == 0 }}" - set_fact: - l_is_containerized: "{{ l_is_atomic or containerized | default(false) | bool }}" + l_is_containerized: "{{ (l_is_atomic | bool) or (containerized | default(false) | bool) }}" - name: Ensure PyYaml is installed action: "{{ ansible_pkg_mgr }} name=PyYAML state=present" @@ -25,4 +25,11 @@ openshift_facts: role: common local_facts: - is_containerized: "{{ containerized | default(None) }}" + # TODO: Deprecate deployment_type in favor of openshift_deployment_type + deployment_type: "{{ openshift_deployment_type | default(deployment_type) }}" + cluster_id: "{{ openshift_cluster_id | default('default') }}" + hostname: "{{ openshift_hostname | default(None) }}" + ip: "{{ openshift_ip | default(None) }}" + is_containerized: "{{ l_is_containerized | default(None) }}" + public_hostname: "{{ openshift_public_hostname | default(None) }}" + public_ip: "{{ openshift_public_ip | default(None) }}" diff --git a/roles/openshift_hosted_logging/README.md b/roles/openshift_hosted_logging/README.md new file mode 100644 index 000000000..b3f363571 --- /dev/null +++ b/roles/openshift_hosted_logging/README.md @@ -0,0 +1,10 @@ +###Required vars: + +- openshift_hosted_logging_hostname: kibana.example.com +- openshift_hosted_logging_elasticsearch_cluster_size: 1 +- openshift_hosted_logging_master_public_url: https://localhost:8443 + +###Optional vars: +- openshift_hosted_logging_secret_vars: (defaults to nothing=/dev/null) kibana.crt=/etc/origin/master/ca.crt kibana.key=/etc/origin/master/ca.key ca.crt=/etc/origin/master/ca.crt ca.key=/etc/origin/master/ca.key +- openshift_hosted_logging_fluentd_replicas: (defaults to 1) 3 +- openshift_hosted_logging_cleanup: (defaults to no) Set this to 'yes' in order to cleanup logging components instead of deploying. diff --git a/roles/openshift_hosted_logging/files/logging-deployer-sa.yaml b/roles/openshift_hosted_logging/files/logging-deployer-sa.yaml new file mode 100644 index 000000000..334c9402b --- /dev/null +++ b/roles/openshift_hosted_logging/files/logging-deployer-sa.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: logging-deployer +secrets: +- name: logging-deployer diff --git a/roles/openshift_hosted_logging/meta/main.yaml b/roles/openshift_hosted_logging/meta/main.yaml new file mode 100644 index 000000000..b695bde87 --- /dev/null +++ b/roles/openshift_hosted_logging/meta/main.yaml @@ -0,0 +1,3 @@ +--- +dependencies: + - { role: openshift_common } diff --git a/roles/openshift_hosted_logging/tasks/cleanup_logging.yaml b/roles/openshift_hosted_logging/tasks/cleanup_logging.yaml new file mode 100644 index 000000000..8331f0389 --- /dev/null +++ b/roles/openshift_hosted_logging/tasks/cleanup_logging.yaml @@ -0,0 +1,59 @@ +--- + - name: Create temp directory for kubeconfig + command: mktemp -d /tmp/openshift-ansible-XXXXXX + register: mktemp + changed_when: False + + - name: Copy the admin client config(s) + command: > + cp {{ openshift_master_config_dir }}/admin.kubeconfig {{ mktemp.stdout }}/admin.kubeconfig + changed_when: False + + - name: "Checking for logging project" + command: "{{ openshift.common.client_binary }} --config={{ mktemp.stdout }}/admin.kubeconfig get project logging" + register: logging_project + failed_when: "'FAILED' in logging_project.stderr" + + - name: "Changing projects" + command: "{{ openshift.common.client_binary }} --config={{ mktemp.stdout }}/admin.kubeconfig project logging" + + + - name: "Cleanup any previous logging infrastructure" + command: "{{ openshift.common.client_binary }} --config={{ mktemp.stdout }}/admin.kubeconfig delete --ignore-not-found all --selector logging-infra={{ item }}" + with_items: + - kibana + - fluentd + - elasticsearch + ignore_errors: yes + + - name: "Cleanup existing support infrastructure" + command: "{{ openshift.common.client_binary }} --config={{ mktemp.stdout }}/admin.kubeconfig delete --ignore-not-found all,sa,oauthclient --selector logging-infra=support" + ignore_errors: yes + + - name: "Cleanup existing secrets" + command: "{{ openshift.common.client_binary }} --config={{ mktemp.stdout }}/admin.kubeconfig delete secret logging-fluentd logging-elasticsearch logging-es-proxy logging-kibana logging-kibana-proxy logging-kibana-ops-proxy" + ignore_errors: yes + register: clean_result + failed_when: clean_result.rc == 1 and 'not found' not in clean_result.stderr + + - name: "Cleanup existing logging deployers" + command: "{{ openshift.common.client_binary }} --config={{ mktemp.stdout }}/admin.kubeconfig delete pods --all" + + + - name: "Cleanup logging project" + command: "{{ openshift.common.client_binary }} --config={{ mktemp.stdout }}/admin.kubeconfig delete project logging" + + + - name: "Remove deployer template" + command: "{{ openshift.common.client_binary }} --config={{ mktemp.stdout }}/admin.kubeconfig delete template logging-deployer-template -n openshift" + register: delete_ouput + failed_when: delete_ouput.rc == 1 and 'exists' not in delete_ouput.stderr + + + - name: Delete temp directory + file: + name: "{{ mktemp.stdout }}" + state: absent + changed_when: False + + - debug: msg="Success!" diff --git a/roles/openshift_hosted_logging/tasks/deploy_logging.yaml b/roles/openshift_hosted_logging/tasks/deploy_logging.yaml new file mode 100644 index 000000000..d8a5b62a0 --- /dev/null +++ b/roles/openshift_hosted_logging/tasks/deploy_logging.yaml @@ -0,0 +1,105 @@ +--- + - fail: msg="This role requires the following vars to be defined. openshift_hosted_logging_master_public_url, openshift_hosted_logging_hostname, openshift_hosted_logging_elasticsearch_cluster_size" + when: "openshift_hosted_logging_hostname is not defined or + openshift_hosted_logging_elasticsearch_cluster_size is not defined or + openshift_hosted_logging_master_public_url is not defined" + + - name: Create temp directory for kubeconfig + command: mktemp -d /tmp/openshift-ansible-XXXXXX + register: mktemp + changed_when: False + + - name: Copy the admin client config(s) + command: > + cp {{ openshift_master_config_dir }}/admin.kubeconfig {{ mktemp.stdout }}/admin.kubeconfig + changed_when: False + + - name: "Create logging project" + command: {{ openshift.common.admin_binary }} --config={{ mktemp.stdout }}/admin.kubeconfig new-project logging + + - name: "Changing projects" + command: "{{ openshift.common.client_binary }} --config={{ mktemp.stdout }}/admin.kubeconfig project logging" + + - name: "Creating logging deployer secret" + command: " {{ openshift.common.client_binary }} --config={{ mktemp.stdout }}/admin.kubeconfig secrets new logging-deployer {{ openshift_hosted_logging_secret_vars | default('nothing=/dev/null') }}" + register: secret_output + failed_when: "secret_output.rc == 1 and 'exists' not in secret_output.stderr" + + - name: "Copy serviceAccount file" + copy: dest=/tmp/logging-deployer-sa.yaml + src={{role_path}}/files/logging-deployer-sa.yaml + force=yes + + - name: "Create logging-deployer service account" + command: "{{ openshift.common.client_binary }} --config={{ mktemp.stdout }}/admin.kubeconfig create -f /tmp/logging-deployer-sa.yaml" + register: deployer_output + failed_when: "deployer_output.rc == 1 and 'exists' not in deployer_output.stderr" + + - name: "Set permissions for logging-deployer service account" + command: "{{ openshift.common.client_binary }} --config={{ mktemp.stdout }}/admin.kubeconfig policy add-role-to-user edit system:serviceaccount:logging:logging-deployer" + register: permiss_output + failed_when: "permiss_output.rc == 1 and 'exists' not in permiss_output.stderr" + + - name: "Set permissions for fluentd" + command: {{ openshift.common.admin_binary}} policy add-scc-to-user privileged system:serviceaccount:logging:aggregated-logging-fluentd + register: fluentd_output + failed_when: "fluentd_output.rc == 1 and 'exists' not in fluentd_output.stderr" + + - name: "Set additional permissions for fluentd" + command: {{ openshift.common.admin_binary}} policy add-cluster-role-to-user cluster-reader system:serviceaccount:logging:aggregated-logging-fluentd + register: fluentd2_output + failed_when: "fluentd2_output.rc == 1 and 'exists' not in fluentd2_output.stderr" + + - name: "Create deployer template" + command: "{{ openshift.common.client_binary }} --config={{ mktemp.stdout }}/admin.kubeconfig create -f /usr/share/openshift/examples/infrastructure-templates/enterprise/logging-deployer.yaml -n openshift" + register: template_output + failed_when: "template_output.rc == 1 and 'exists' not in template_output.stderr" + + - name: "Process the deployer template" + shell: "{{ openshift.common.client_binary }} --config={{ mktemp.stdout }}/admin.kubeconfig process logging-deployer-template -n openshift -v {{ oc_process_values }} | {{ openshift.common.client_binary }} --config={{ mktemp.stdout }}/admin.kubeconfig create -f -" + + - name: "Wait for image pull and deployer pod" + shell: "{{ openshift.common.client_binary }} --config={{ mktemp.stdout }}/admin.kubeconfig get pods | grep logging-deployer.*Completed" + register: result + until: result.rc == 0 + retries: 15 + delay: 10 + + - name: "Process support template" + shell: "{{ openshift.common.client_binary }} --config={{ mktemp.stdout }}/admin.kubeconfig process logging-support-template | {{ openshift.common.client_binary }} --config={{ mktemp.stdout }}/admin.kubeconfig create -f -" + + - name: "Set insecured registry" + command: "{{ openshift.common.client_binary }} --config={{ mktemp.stdout }}/admin.kubeconfig annotate is --all openshift.io/image.insecureRepository=true --overwrite" + when: "target_registry is defined and insecure_registry == 'true'" + + - name: "Wait for imagestreams to become available" + shell: "{{ openshift.common.client_binary }} --config={{ mktemp.stdout }}/admin.kubeconfig get is | grep logging-fluentd" + register: result + until: result.rc == 0 + failed_when: result.rc == 1 and 'not found' not in result.stderr + retries: 20 + delay: 10 + + - name: "Wait for replication controllers to become available" + shell: "{{ openshift.common.client_binary }} --config={{ mktemp.stdout }}/admin.kubeconfig get rc | grep logging-fluentd-1" + register: result + until: result.rc == 0 + failed_when: result.rc == 1 and 'not found' not in result.stderr + retries: 20 + delay: 10 + + + - name: "Scale fluentd deployment config" + command: "{{ openshift.common.client_binary }} --config={{ mktemp.stdout }}/admin.kubeconfig scale dc/logging-fluentd --replicas={{ fluentd_replicas | default('1') }}" + + + - name: "Scale fluentd replication controller" + command: "{{ openshift.common.client_binary }} --config={{ mktemp.stdout }}/admin.kubeconfig scale rc/logging-fluentd-1 --replicas={{ fluentd_replicas | default('1') }}" + + - debug: msg="Logging components deployed. Note persistant volume for elasticsearch must be setup manually" + + - name: Delete temp directory + file: + name: "{{ mktemp.stdout }}" + state: absent + changed_when: False diff --git a/roles/openshift_hosted_logging/tasks/main.yaml b/roles/openshift_hosted_logging/tasks/main.yaml new file mode 100644 index 000000000..42568597a --- /dev/null +++ b/roles/openshift_hosted_logging/tasks/main.yaml @@ -0,0 +1,8 @@ +--- +- name: Cleanup logging deployment + include: "{{ role_path }}/tasks/cleanup_logging.yaml" + when: openshift_hosted_logging_cleanup | default(false) | bool + +- name: Deploy logging + include: "{{ role_path }}/tasks/deploy_logging.yaml" + when: not openshift_hosted_logging_cleanup | default(false) | bool diff --git a/roles/openshift_hosted_logging/vars/main.yaml b/roles/openshift_hosted_logging/vars/main.yaml new file mode 100644 index 000000000..586c2ab91 --- /dev/null +++ b/roles/openshift_hosted_logging/vars/main.yaml @@ -0,0 +1,6 @@ +kh_kv: "KIBANA_HOSTNAME={{ openshift_hosted_logging_hostname | quote }}" +es_cs_kv: "ES_CLUSTER_SIZE={{ openshift_hosted_logging_elasticsearch_cluster_size | quote }}" +pmu_kv: "PUBLIC_MASTER_URL={{ openshift_hosted_logging_master_public_url | quote }}" +ip_kv: "{{ 'IMAGE_PREFIX=' ~ target_registry | quote if target_registry is defined else '' }}" +oc_process_values: "{{ kh_kv }},{{ es_cs_kv }},{{ pmu_kv }},{{ ip_kv }}" +openshift_master_config_dir: "{{ openshift.common.config_base }}/master" diff --git a/roles/openshift_manageiq/tasks/main.yaml b/roles/openshift_manageiq/tasks/main.yaml index d2ff1b4b7..2a651df65 100644 --- a/roles/openshift_manageiq/tasks/main.yaml +++ b/roles/openshift_manageiq/tasks/main.yaml @@ -18,7 +18,7 @@ failed_when: "'already exists' not in osmiq_create_mi_project.stderr and osmiq_create_mi_project.rc != 0" changed_when: osmiq_create_mi_project.rc == 0 -- name: Create Service Account +- name: Create Admin Service Account shell: > echo {{ manageiq_service_account | to_json | quote }} | {{ openshift.common.client_binary }} create @@ -29,6 +29,17 @@ failed_when: "'already exists' not in osmiq_create_service_account.stderr and osmiq_create_service_account.rc != 0" changed_when: osmiq_create_service_account.rc == 0 +- name: Create Image Inspector Service Account + shell: > + echo {{ manageiq_image_inspector_service_account | to_json | quote }} | + {{ openshift.common.client_binary }} create + -n management-infra + --config={{manage_iq_tmp_conf}} + -f - + register: osmiq_create_service_account + failed_when: "'already exists' not in osmiq_create_service_account.stderr and osmiq_create_service_account.rc != 0" + changed_when: osmiq_create_service_account.rc == 0 + - name: Create Cluster Role shell: > echo {{ manageiq_cluster_role | to_json | quote }} | diff --git a/roles/openshift_manageiq/vars/main.yml b/roles/openshift_manageiq/vars/main.yml index 77e1c304b..69ee2cb4c 100644 --- a/roles/openshift_manageiq/vars/main.yml +++ b/roles/openshift_manageiq/vars/main.yml @@ -15,6 +15,12 @@ manageiq_service_account: metadata: name: management-admin +manageiq_image_inspector_service_account: + apiVersion: v1 + kind: ServiceAccount + metadata: + name: inspector-admin + manage_iq_tmp_conf: /tmp/manageiq_admin.kubeconfig manage_iq_tasks: @@ -22,3 +28,5 @@ manage_iq_tasks: - policy add-role-to-user -n management-infra management-infra-admin -z management-admin - policy add-cluster-role-to-user cluster-reader system:serviceaccount:management-infra:management-admin - policy add-scc-to-user privileged system:serviceaccount:management-infra:management-admin + - policy add-cluster-role-to-user system:image-puller system:serviceaccount:management-infra:inspector-admin + - policy add-scc-to-user privileged system:serviceaccount:management-infra:inspector-admin diff --git a/roles/openshift_master/handlers/main.yml b/roles/openshift_master/handlers/main.yml index 4d1216aae..70c7ef4e4 100644 --- a/roles/openshift_master/handlers/main.yml +++ b/roles/openshift_master/handlers/main.yml @@ -1,23 +1,24 @@ --- - name: restart master service: name={{ openshift.common.service_type }}-master state=restarted - when: (not openshift_master_ha | bool) and (not (master_service_status_changed | default(false) | bool)) + when: (openshift.master.ha is defined and not openshift.master.ha | bool) and (not (master_service_status_changed | default(false) | bool)) notify: Verify API Server - name: restart master api service: name={{ openshift.common.service_type }}-master-api state=restarted - when: (openshift_master_ha | bool) and (not (master_api_service_status_changed | default(false) | bool)) and openshift.master.cluster_method == 'native' + when: (openshift.master.ha is defined and openshift_master_ha | bool) and (not (master_api_service_status_changed | default(false) | bool)) and openshift.master.cluster_method == 'native' notify: Verify API Server - name: restart master controllers service: name={{ openshift.common.service_type }}-master-controllers state=restarted - when: (openshift_master_ha | bool) and (not (master_controllers_service_status_changed | default(false) | bool)) and openshift.master.cluster_method == 'native' + when: (openshift.master.ha is defined and openshift_master_ha | bool) and (not (master_controllers_service_status_changed | default(false) | bool)) and openshift.master.cluster_method == 'native' - name: Verify API Server # Using curl here since the uri module requires python-httplib2 and # wait_for port doesn't provide health information. command: > - curl -k --silent {{ openshift.master.api_url }}/healthz/ready + curl --silent --cacert {{ openshift.common.config_base }}/master/ca.crt + {{ openshift.master.api_url }}/healthz/ready register: api_available_output until: api_available_output.stdout == 'ok' retries: 120 diff --git a/roles/openshift_master/meta/main.yml b/roles/openshift_master/meta/main.yml index 8db99fc2a..7ab913eea 100644 --- a/roles/openshift_master/meta/main.yml +++ b/roles/openshift_master/meta/main.yml @@ -12,5 +12,7 @@ galaxy_info: categories: - cloud dependencies: -- { role: openshift_common } -- { role: openshift_cli } +- role: openshift_docker +- role: openshift_cli +- role: openshift_cloud_provider +- role: openshift_master_facts diff --git a/roles/openshift_master/tasks/main.yml b/roles/openshift_master/tasks/main.yml index 759cda7d0..23994cdcf 100644 --- a/roles/openshift_master/tasks/main.yml +++ b/roles/openshift_master/tasks/main.yml @@ -23,71 +23,6 @@ msg: "Pacemaker based HA is not supported at this time when used with containerized installs" when: openshift_master_ha | bool and openshift_master_cluster_method == "pacemaker" and openshift.common.is_containerized | bool -- name: Set master facts - openshift_facts: - role: master - local_facts: - cluster_method: "{{ openshift_master_cluster_method | default(None) }}" - cluster_hostname: "{{ openshift_master_cluster_hostname | default(None) }}" - cluster_public_hostname: "{{ openshift_master_cluster_public_hostname | default(None) }}" - debug_level: "{{ openshift_master_debug_level | default(openshift.common.debug_level) }}" - api_port: "{{ openshift_master_api_port | default(None) }}" - api_url: "{{ openshift_master_api_url | default(None) }}" - api_use_ssl: "{{ openshift_master_api_use_ssl | default(None) }}" - public_api_url: "{{ openshift_master_public_api_url | default(None) }}" - console_path: "{{ openshift_master_console_path | default(None) }}" - console_port: "{{ openshift_master_console_port | default(None) }}" - console_url: "{{ openshift_master_console_url | default(None) }}" - console_use_ssl: "{{ openshift_master_console_use_ssl | default(None) }}" - public_console_url: "{{ openshift_master_public_console_url | default(None) }}" - logging_public_url: "{{ openshift_master_logging_public_url | default(None) }}" - metrics_public_url: "{{ openshift_master_metrics_public_url | default(None) }}" - logout_url: "{{ openshift_master_logout_url | default(None) }}" - extension_scripts: "{{ openshift_master_extension_scripts | default(None) }}" - extension_stylesheets: "{{ openshift_master_extension_stylesheets | default(None) }}" - extensions: "{{ openshift_master_extensions | default(None) }}" - oauth_template: "{{ openshift_master_oauth_template | default(None) }}" - etcd_hosts: "{{ openshift_master_etcd_hosts | default(None) }}" - etcd_port: "{{ openshift_master_etcd_port | default(None) }}" - etcd_use_ssl: "{{ openshift_master_etcd_use_ssl | default(None) }}" - etcd_urls: "{{ openshift_master_etcd_urls | default(None) }}" - embedded_etcd: "{{ openshift_master_embedded_etcd | default(None) }}" - embedded_kube: "{{ openshift_master_embedded_kube | default(None) }}" - embedded_dns: "{{ openshift_master_embedded_dns | default(None) }}" - dns_port: "{{ openshift_master_dns_port | default(None) }}" - bind_addr: "{{ openshift_master_bind_addr | default(None) }}" - pod_eviction_timeout: "{{ openshift_master_pod_eviction_timeout | default(None) }}" - portal_net: "{{ openshift_master_portal_net | default(None) }}" - session_max_seconds: "{{ openshift_master_session_max_seconds | default(None) }}" - session_name: "{{ openshift_master_session_name | default(None) }}" - session_secrets_file: "{{ openshift_master_session_secrets_file | default(None) }}" - session_auth_secrets: "{{ openshift_master_session_auth_secrets | default(None) }}" - session_encryption_secrets: "{{ openshift_master_session_encryption_secrets | default(None) }}" - access_token_max_seconds: "{{ openshift_master_access_token_max_seconds | default(None) }}" - auth_token_max_seconds: "{{ openshift_master_auth_token_max_seconds | default(None) }}" - identity_providers: "{{ openshift_master_identity_providers | default(None) }}" - registry_url: "{{ oreg_url | default(None) }}" - oauth_grant_method: "{{ openshift_master_oauth_grant_method | default(None) }}" - sdn_cluster_network_cidr: "{{ osm_cluster_network_cidr | default(None) }}" - sdn_host_subnet_length: "{{ osm_host_subnet_length | default(None) }}" - default_subdomain: "{{ openshift_master_default_subdomain | default(osm_default_subdomain) | default(None) }}" - custom_cors_origins: "{{ osm_custom_cors_origins | default(None) }}" - default_node_selector: "{{ osm_default_node_selector | default(None) }}" - project_request_message: "{{ osm_project_request_message | default(None) }}" - project_request_template: "{{ osm_project_request_template | default(None) }}" - mcs_allocator_range: "{{ osm_mcs_allocator_range | default(None) }}" - mcs_labels_per_project: "{{ osm_mcs_labels_per_project | default(None) }}" - uid_allocator_range: "{{ osm_uid_allocator_range | default(None) }}" - router_selector: "{{ openshift_router_selector | default(None) }}" - registry_selector: "{{ openshift_registry_selector | default(None) }}" - api_server_args: "{{ osm_api_server_args | default(None) }}" - controller_args: "{{ osm_controller_args | default(None) }}" - infra_nodes: "{{ openshift_infra_nodes | default(None) }}" - disabled_features: "{{ osm_disabled_features | default(None) }}" - master_count: "{{ openshift_master_count | default(None) }}" - controller_lease_ttl: "{{ osm_controller_lease_ttl | default(None) }}" - master_image: "{{ osm_image | default(None) }}" - - name: Install Master package action: "{{ ansible_pkg_mgr }} name={{ openshift.common.service_type }}-master{{ openshift_version }} state=present" when: not openshift.common.is_containerized | bool @@ -97,13 +32,6 @@ docker pull {{ openshift.master.master_image }}:{{ openshift_version }} when: openshift.common.is_containerized | bool -- name: Install Master docker service file - template: - dest: "/etc/systemd/system/{{ openshift.common.service_type }}-master.service" - src: docker/master.docker.service.j2 - register: install_result - when: openshift.common.is_containerized | bool and not openshift_master_ha | bool - - name: Create openshift.common.data_dir file: path: "{{ openshift.common.data_dir }}" @@ -137,9 +65,9 @@ - restart master controllers - name: Create the scheduler config - template: + copy: + content: "{{ scheduler_config | to_nice_json }}" dest: "{{ openshift_master_scheduler_conf }}" - src: scheduler.json.j2 backup: true notify: - restart master @@ -168,54 +96,8 @@ when: item.kind == 'HTPasswdPasswordIdentityProvider' with_items: openshift.master.identity_providers -- name: Init HA Service Info - set_fact: - ha_suffix: "" - ha_svcdir: "/usr/lib/systemd/system" - -- name: Set HA Service Info for containerized installs - set_fact: - ha_suffix: ".docker" - ha_svcdir: "/etc/systemd/system" - when: openshift.common.is_containerized | bool - -# workaround for missing systemd unit files for controllers/api -- name: Create the systemd unit files - template: - src: "{{ ha_svc_template_path }}/atomic-openshift-master-{{ item }}.service.j2" - dest: "{{ ha_svcdir }}/{{ openshift.common.service_type }}-master-{{ item }}.service" - when: openshift_master_ha | bool and openshift_master_cluster_method == "native" - with_items: - - api - - controllers - register: create_unit_files - -- command: systemctl daemon-reload - when: create_unit_files | changed -# end workaround for missing systemd unit files - -- name: Create the master api service env file - template: - src: "{{ ha_svc_template_path }}/atomic-openshift-master-api.j2" - dest: /etc/sysconfig/{{ openshift.common.service_type }}-master-api - when: openshift_master_ha | bool and openshift_master_cluster_method == "native" - notify: - - restart master api - -- name: Create the master controllers service env file - template: - src: "{{ ha_svc_template_path }}/atomic-openshift-master-controllers.j2" - dest: /etc/sysconfig/{{ openshift.common.service_type }}-master-controllers - when: openshift_master_ha | bool and openshift_master_cluster_method == "native" - notify: - - restart master controllers - -- name: Create the master service env file - template: - src: "atomic-openshift-master.j2" - dest: /etc/sysconfig/{{ openshift.common.service_type }}-master - notify: - - restart master +- name: Install the systemd units + include: systemd_units.yml - name: Create session secrets file template: @@ -282,7 +164,8 @@ # Using curl here since the uri module requires python-httplib2 and # wait_for port doesn't provide health information. command: > - curl -k --silent {{ openshift.master.api_url }}/healthz/ready + curl --silent --cacert {{ openshift.common.config_base }}/master/ca.crt + {{ openshift.master.api_url }}/healthz/ready register: api_available_output until: api_available_output.stdout == 'ok' retries: 120 @@ -319,6 +202,9 @@ changed_when: false register: _ansible_ssh_user_gid +- set_fact: + client_users: "{{ [ansible_ssh_user, 'root'] | unique }}" + - name: Create the client config dir(s) file: path: "~{{ item }}/.kube" @@ -326,9 +212,7 @@ mode: 0700 owner: "{{ item }}" group: "{{ 'root' if item == 'root' else _ansible_ssh_user_gid.stdout }}" - with_items: - - root - - "{{ ansible_ssh_user }}" + with_items: client_users # TODO: Update this file if the contents of the source file are not present in # the dest file, will need to make sure to ignore things that could be added @@ -336,9 +220,7 @@ command: cp {{ openshift_master_config_dir }}/admin.kubeconfig ~{{ item }}/.kube/config args: creates: ~{{ item }}/.kube/config - with_items: - - root - - "{{ ansible_ssh_user }}" + with_items: client_users - name: Update the permissions on the admin client config(s) file: @@ -347,6 +229,4 @@ mode: 0700 owner: "{{ item }}" group: "{{ 'root' if item == 'root' else _ansible_ssh_user_gid.stdout }}" - with_items: - - root - - "{{ ansible_ssh_user }}" + with_items: client_users diff --git a/roles/openshift_master/tasks/systemd_units.yml b/roles/openshift_master/tasks/systemd_units.yml new file mode 100644 index 000000000..a81270bab --- /dev/null +++ b/roles/openshift_master/tasks/systemd_units.yml @@ -0,0 +1,69 @@ +# This file is included both in the openshift_master role and in the upgrade +# playbooks. For that reason the ha_svc variables are use set_fact instead of +# the vars directory on the role. + +- name: Init HA Service Info + set_fact: + containerized_svc_dir: "/usr/lib/systemd/system" + ha_svc_template_path: "native-cluster" + +- name: Set HA Service Info for containerized installs + set_fact: + containerized_svc_dir: "/etc/systemd/system" + ha_svc_template_path: "docker-cluster" + when: openshift.common.is_containerized | bool + +# workaround for missing systemd unit files +- name: Create the systemd unit files + template: + src: "docker/master.docker.service.j2" + dest: "{{ containerized_svc_dir }}/{{ openshift.common.service_type }}-master.service" + when: openshift.common.is_containerized | bool and (openshift.master.ha is not defined or not openshift.master.ha | bool) + register: create_master_unit_file + +- command: systemctl daemon-reload + when: create_master_unit_file | changed + +- name: Create the ha systemd unit files + template: + src: "{{ ha_svc_template_path }}/atomic-openshift-master-{{ item }}.service.j2" + dest: "{{ containerized_svc_dir }}/{{ openshift.common.service_type }}-master-{{ item }}.service" + when: openshift.master.ha is defined and openshift.master.ha | bool and openshift_master_cluster_method == "native" + with_items: + - api + - controllers + register: create_ha_unit_files + +- command: systemctl daemon-reload + when: create_ha_unit_files | changed +# end workaround for missing systemd unit files + +- name: Create the master api service env file + template: + src: "{{ ha_svc_template_path }}/atomic-openshift-master-api.j2" + dest: /etc/sysconfig/{{ openshift.common.service_type }}-master-api + when: openshift.master.ha is defined and openshift.master.ha | bool and openshift_master_cluster_method == "native" + notify: + - restart master api + +- name: Create the master controllers service env file + template: + src: "{{ ha_svc_template_path }}/atomic-openshift-master-controllers.j2" + dest: /etc/sysconfig/{{ openshift.common.service_type }}-master-controllers + when: openshift.master.ha is defined and openshift.master.ha | bool and openshift_master_cluster_method == "native" + notify: + - restart master controllers + +- name: Install Master docker service file + template: + dest: "/etc/systemd/system/{{ openshift.common.service_type }}-master.service" + src: docker/master.docker.service.j2 + register: install_result + when: openshift.common.is_containerized | bool and openshift.master.ha is defined and not openshift.master.ha | bool + +- name: Create the master service env file + template: + src: "atomic-openshift-master.j2" + dest: /etc/sysconfig/{{ openshift.common.service_type }}-master + notify: + - restart master diff --git a/roles/openshift_master/templates/atomic-openshift-master.j2 b/roles/openshift_master/templates/atomic-openshift-master.j2 index 8602a492e..7f1576682 100644 --- a/roles/openshift_master/templates/atomic-openshift-master.j2 +++ b/roles/openshift_master/templates/atomic-openshift-master.j2 @@ -1,9 +1,14 @@ OPTIONS=--loglevel={{ openshift.master.debug_level }} CONFIG_FILE={{ openshift_master_config_file }} -{% if openshift.common.is_containerized %} +{% if openshift.common.is_containerized | bool %} IMAGE_VERSION={{ openshift_version }} {% endif %} +{% if 'cloudprovider' in openshift and 'aws' in openshift.cloudprovider and openshift.cloudprovider.kind == 'aws' and 'access_key' in openshift.cloudprovider.aws and 'secret_key' in openshift.cloudprovider.aws %} +AWS_ACCESS_KEY_ID={{ openshift.cloudprovider.aws.access_key }} +AWS_SECRET_ACCESS_KEY={{ openshift.cloudprovider.aws.secret_key }} +{% endif %} + # Proxy configuration # Origin uses standard HTTP_PROXY environment variables. Be sure to set # NO_PROXY for your master diff --git a/roles/openshift_master/templates/docker-cluster/atomic-openshift-master-api.service.j2 b/roles/openshift_master/templates/docker-cluster/atomic-openshift-master-api.service.j2 index 9c1f5a300..5e6577d95 100644 --- a/roles/openshift_master/templates/docker-cluster/atomic-openshift-master-api.service.j2 +++ b/roles/openshift_master/templates/docker-cluster/atomic-openshift-master-api.service.j2 @@ -1,26 +1,26 @@ [Unit] Description=Atomic OpenShift Master API Documentation=https://github.com/openshift/origin -After=network.target -After=etcd.service +After=etcd_container.service +Wants=etcd_container.service Before={{ openshift.common.service_type }}-node.service -Requires=network.target -Requires=docker.service +After=docker.service PartOf=docker.service +Requires=docker.service [Service] EnvironmentFile=/etc/sysconfig/{{ openshift.common.service_type }}-master-api Environment=GOTRACEBACK=crash ExecStartPre=-/usr/bin/docker rm -f {{ openshift.common.service_type}}-master-api -ExecStart=/usr/bin/docker run --rm --privileged --net=host --name {{ openshift.common.service_type }}-master-api -v {{ openshift.common.data_dir }}:{{ openshift.common.data_dir }} -v /var/run/docker.sock:/var/run/docker.sock -v {{ openshift.common.config_base }}:{{ openshift.common.config_base }} {{ openshift.master.master_image }}:${IMAGE_VERSION} start master api --config=${CONFIG_FILE} $OPTIONS +ExecStart=/usr/bin/docker run --rm --privileged --net=host --name {{ openshift.common.service_type }}-master-api --env-file=/etc/sysconfig/{{ openshift.common.service_type }}-master-api -v {{ openshift.common.data_dir }}:{{ openshift.common.data_dir }} -v /var/run/docker.sock:/var/run/docker.sock -v {{ openshift.common.config_base }}:{{ openshift.common.config_base }} {{ openshift.master.master_image }}:${IMAGE_VERSION} start master api --config=${CONFIG_FILE} $OPTIONS ExecStartPost=/usr/bin/sleep 10 ExecStop=/usr/bin/docker stop {{ openshift.common.service_type }}-master-api LimitNOFILE=131072 LimitCORE=infinity WorkingDirectory={{ openshift.common.data_dir }} -SyslogIdentifier=atomic-openshift-master-api +SyslogIdentifier={{ openshift.common.service_type }}-master-api Restart=always [Install] -WantedBy=multi-user.target +WantedBy=docker.service WantedBy={{ openshift.common.service_type }}-node.service diff --git a/roles/openshift_master/templates/docker-cluster/atomic-openshift-master-controllers.service.j2 b/roles/openshift_master/templates/docker-cluster/atomic-openshift-master-controllers.service.j2 index 915c0cb8c..04c84a84a 100644 --- a/roles/openshift_master/templates/docker-cluster/atomic-openshift-master-controllers.service.j2 +++ b/roles/openshift_master/templates/docker-cluster/atomic-openshift-master-controllers.service.j2 @@ -1,9 +1,9 @@ [Unit] Description=Atomic OpenShift Master Controllers Documentation=https://github.com/openshift/origin -After=network.target +Wants={{ openshift.common.service_type }}-master-api.service After={{ openshift.common.service_type }}-master-api.service -Before={{ openshift.common.service_type }}-node.service +After=docker.service Requires=docker.service PartOf=docker.service @@ -11,7 +11,7 @@ PartOf=docker.service EnvironmentFile=/etc/sysconfig/{{ openshift.common.service_type }}-master-controllers Environment=GOTRACEBACK=crash ExecStartPre=-/usr/bin/docker rm -f {{ openshift.common.service_type}}-master-controllers -ExecStart=/usr/bin/docker run --rm --privileged --net=host --name {{ openshift.common.service_type }}-master-controllers -v {{ openshift.common.data_dir }}:{{ openshift.common.data_dir }} -v /var/run/docker.sock:/var/run/docker.sock -v {{ openshift.common.config_base }}:{{ openshift.common.config_base }} {{ openshift.master.master_image }}:${IMAGE_VERSION} start master controllers --config=${CONFIG_FILE} $OPTIONS +ExecStart=/usr/bin/docker run --rm --privileged --net=host --name {{ openshift.common.service_type }}-master-controllers --env-file=/etc/sysconfig/{{ openshift.common.service_type }}-master-controllers -v {{ openshift.common.data_dir }}:{{ openshift.common.data_dir }} -v /var/run/docker.sock:/var/run/docker.sock -v {{ openshift.common.config_base }}:{{ openshift.common.config_base }} {{ openshift.master.master_image }}:${IMAGE_VERSION} start master controllers --config=${CONFIG_FILE} $OPTIONS ExecStartPost=/usr/bin/sleep 10 ExecStop=/usr/bin/docker stop {{ openshift.common.service_type }}-master-controllers LimitNOFILE=131072 @@ -21,5 +21,4 @@ SyslogIdentifier={{ openshift.common.service_type }}-master-controllers Restart=on-failure [Install] -WantedBy=multi-user.target -WantedBy={{ openshift.common.service_type }}-node.service +WantedBy=docker.service diff --git a/roles/openshift_master/templates/docker/master.docker.service.j2 b/roles/openshift_master/templates/docker/master.docker.service.j2 index f88262567..6bd0dcf56 100644 --- a/roles/openshift_master/templates/docker/master.docker.service.j2 +++ b/roles/openshift_master/templates/docker/master.docker.service.j2 @@ -1,16 +1,17 @@ [Unit] After=docker.service -Before={{ openshift.common.service_type }}-node.service Requires=docker.service PartOf=docker.service +After=etcd_container.service +Wants=etcd_container.service [Service] EnvironmentFile=/etc/sysconfig/{{ openshift.common.service_type }}-master ExecStartPre=-/usr/bin/docker rm -f {{ openshift.common.service_type }}-master -ExecStart=/usr/bin/docker run --rm --privileged --net=host --name {{ openshift.common.service_type }}-master -v {{ openshift.common.data_dir }}:{{ openshift.common.data_dir }} -v /var/run/docker.sock:/var/run/docker.sock -v {{ openshift.common.config_base }}:{{ openshift.common.config_base }} {{ openshift.master.master_image }}:${IMAGE_VERSION} start master --config=${CONFIG_FILE} $OPTIONS +ExecStart=/usr/bin/docker run --rm --privileged --net=host --name {{ openshift.common.service_type }}-master --env-file=/etc/sysconfig/{{ openshift.common.service_type }}-master -v {{ openshift.common.data_dir }}:{{ openshift.common.data_dir }} -v /var/run/docker.sock:/var/run/docker.sock -v {{ openshift.common.config_base }}:{{ openshift.common.config_base }} {{ openshift.master.master_image }}:${IMAGE_VERSION} start master --config=${CONFIG_FILE} $OPTIONS ExecStartPost=/usr/bin/sleep 10 ExecStop=/usr/bin/docker stop {{ openshift.common.service_type }}-master Restart=always [Install] -WantedBy=multi-user.target +WantedBy=docker.service diff --git a/roles/openshift_master/templates/native-cluster/atomic-openshift-master-api.j2 b/roles/openshift_master/templates/native-cluster/atomic-openshift-master-api.j2 index f953de404..fa2323a2c 100644 --- a/roles/openshift_master/templates/native-cluster/atomic-openshift-master-api.j2 +++ b/roles/openshift_master/templates/native-cluster/atomic-openshift-master-api.j2 @@ -1,9 +1,14 @@ OPTIONS=--loglevel={{ openshift.master.debug_level }} --listen={{ 'https' if openshift.master.api_use_ssl else 'http' }}://{{ openshift.master.bind_addr }}:{{ openshift.master.api_port }} --master={{ openshift.master.loopback_api_url }} CONFIG_FILE={{ openshift_master_config_file }} -{% if openshift.common.is_containerized %} +{% if openshift.common.is_containerized | bool %} IMAGE_VERSION={{ openshift_version }} {% endif %} +{% if 'cloudprovider' in openshift and 'aws' in openshift.cloudprovider and openshift.cloudprovider.kind == 'aws' and 'access_key' in openshift.cloudprovider.aws and 'secret_key' in openshift.cloudprovider.aws %} +AWS_ACCESS_KEY_ID={{ openshift.cloudprovider.aws.access_key }} +AWS_SECRET_ACCESS_KEY={{ openshift.cloudprovider.aws.secret_key }} +{% endif %} + # Proxy configuration # Origin uses standard HTTP_PROXY environment variables. Be sure to set # NO_PROXY for your master diff --git a/roles/openshift_master/templates/native-cluster/atomic-openshift-master-api.service.j2 b/roles/openshift_master/templates/native-cluster/atomic-openshift-master-api.service.j2 index ba19fb348..e61418eb6 100644 --- a/roles/openshift_master/templates/native-cluster/atomic-openshift-master-api.service.j2 +++ b/roles/openshift_master/templates/native-cluster/atomic-openshift-master-api.service.j2 @@ -1,10 +1,10 @@ [Unit] Description=Atomic OpenShift Master API Documentation=https://github.com/openshift/origin -After=network.target +After=network-online.target After=etcd.service Before={{ openshift.common.service_type }}-node.service -Requires=network.target +Requires=network-online.target [Service] Type=notify diff --git a/roles/openshift_master/templates/native-cluster/atomic-openshift-master-controllers.j2 b/roles/openshift_master/templates/native-cluster/atomic-openshift-master-controllers.j2 index 30da9d396..632dfbb8a 100644 --- a/roles/openshift_master/templates/native-cluster/atomic-openshift-master-controllers.j2 +++ b/roles/openshift_master/templates/native-cluster/atomic-openshift-master-controllers.j2 @@ -1,9 +1,14 @@ OPTIONS=--loglevel={{ openshift.master.debug_level }} --listen={{ 'https' if openshift.master.api_use_ssl else 'http' }}://{{ openshift.master.bind_addr }}:{{ openshift.master.controllers_port }} CONFIG_FILE={{ openshift_master_config_file }} -{% if openshift.common.is_containerized %} +{% if openshift.common.is_containerized | bool %} IMAGE_VERSION={{ openshift_version }} {% endif %} +{% if 'cloudprovider' in openshift and 'aws' in openshift.cloudprovider and openshift.cloudprovider.kind == 'aws' and 'access_key' in openshift.cloudprovider.aws and 'secret_key' in openshift.cloudprovider.aws %} +AWS_ACCESS_KEY_ID={{ openshift.cloudprovider.aws.access_key }} +AWS_SECRET_ACCESS_KEY={{ openshift.cloudprovider.aws.secret_key }} +{% endif %} + # Proxy configuration # Origin uses standard HTTP_PROXY environment variables. Be sure to set # NO_PROXY for your master diff --git a/roles/openshift_master/templates/native-cluster/atomic-openshift-master-controllers.service.j2 b/roles/openshift_master/templates/native-cluster/atomic-openshift-master-controllers.service.j2 index b393bb9ff..ba4c1e0e8 100644 --- a/roles/openshift_master/templates/native-cluster/atomic-openshift-master-controllers.service.j2 +++ b/roles/openshift_master/templates/native-cluster/atomic-openshift-master-controllers.service.j2 @@ -1,10 +1,10 @@ [Unit] Description=Atomic OpenShift Master Controllers Documentation=https://github.com/openshift/origin -After=network.target +After=network-online.target After={{ openshift.common.service_type }}-master-api.service -Before={{ openshift.common.service_type }}-node.service -Requires=network.target +Wants={{ openshift.common.service_type }}-master-api.service +Requires=network-online.target [Service] {% if openshift.common.version_gte_3_1_1_or_1_1_1 | bool %} @@ -23,4 +23,3 @@ Restart=on-failure [Install] WantedBy=multi-user.target -WantedBy={{ openshift.common.service_type }}-node.service diff --git a/roles/openshift_master/templates/scheduler.json.j2 b/roles/openshift_master/templates/scheduler.json.j2 deleted file mode 100644 index cb5f43bb2..000000000 --- a/roles/openshift_master/templates/scheduler.json.j2 +++ /dev/null @@ -1,15 +0,0 @@ -{ - "kind": "Policy", - "apiVersion": "v1", - "predicates": [ - {"name": "MatchNodeSelector"}, - {"name": "PodFitsResources"}, - {"name": "PodFitsPorts"}, - {"name": "NoDiskConflict"}, - {"name": "Region", "argument": {"serviceAffinity" : {"labels" : ["region"]}}} - ],"priorities": [ - {"name": "LeastRequestedPriority", "weight": 1}, - {"name": "ServiceSpreadingPriority", "weight": 1}, - {"name": "Zone", "weight" : 2, "argument": {"serviceAntiAffinity" : {"label": "zone"}}} - ] -} diff --git a/roles/openshift_master/vars/main.yml b/roles/openshift_master/vars/main.yml index 6b5a73238..198f9235d 100644 --- a/roles/openshift_master/vars/main.yml +++ b/roles/openshift_master/vars/main.yml @@ -8,8 +8,11 @@ openshift_master_session_secrets_file: "{{ openshift_master_config_dir }}/sessio openshift_master_policy: "{{ openshift_master_config_dir }}/policy.json" openshift_version: "{{ openshift_pkg_version | default(openshift_image_tag) | default(openshift.common.image_tag) | default('') }}" -ha_svc_template_path: "{{ 'docker-cluster' if openshift.common.is_containerized | bool else 'native-cluster' }}" -ha_svc_svc_dir: "{{ '/etc/systemd/system' if openshift.common.is_containerized | bool else '/usr/lib/systemd/system' }}" +scheduler_config: + kind: Policy + apiVersion: v1 + predicates: "{{ openshift.master.scheduler_predicates }}" + priorities: "{{ openshift.master.scheduler_priorities }}" openshift_master_valid_grant_methods: - auto diff --git a/roles/openshift_master_facts/meta/main.yml b/roles/openshift_master_facts/meta/main.yml new file mode 100644 index 000000000..9dbf719f8 --- /dev/null +++ b/roles/openshift_master_facts/meta/main.yml @@ -0,0 +1,15 @@ +--- +galaxy_info: + author: Jason DeTiberus + description: OpenShift Master Facts + company: Red Hat, Inc. + license: Apache License, Version 2.0 + min_ansible_version: 1.9 + platforms: + - name: EL + versions: + - 7 + categories: + - cloud +dependencies: +- role: openshift_facts diff --git a/roles/openshift_master_facts/tasks/main.yml b/roles/openshift_master_facts/tasks/main.yml new file mode 100644 index 000000000..2a3e38af4 --- /dev/null +++ b/roles/openshift_master_facts/tasks/main.yml @@ -0,0 +1,67 @@ +--- +- name: Set master facts + openshift_facts: + role: master + local_facts: + cluster_method: "{{ openshift_master_cluster_method | default(None) }}" + cluster_hostname: "{{ openshift_master_cluster_hostname | default(None) }}" + cluster_public_hostname: "{{ openshift_master_cluster_public_hostname | default(None) }}" + debug_level: "{{ openshift_master_debug_level | default(openshift.common.debug_level) }}" + api_port: "{{ openshift_master_api_port | default(None) }}" + api_url: "{{ openshift_master_api_url | default(None) }}" + api_use_ssl: "{{ openshift_master_api_use_ssl | default(None) }}" + public_api_url: "{{ openshift_master_public_api_url | default(None) }}" + console_path: "{{ openshift_master_console_path | default(None) }}" + console_port: "{{ openshift_master_console_port | default(None) }}" + console_url: "{{ openshift_master_console_url | default(None) }}" + console_use_ssl: "{{ openshift_master_console_use_ssl | default(None) }}" + public_console_url: "{{ openshift_master_public_console_url | default(None) }}" + logging_public_url: "{{ openshift_master_logging_public_url | default(None) }}" + metrics_public_url: "{{ openshift_master_metrics_public_url | default(None) }}" + logout_url: "{{ openshift_master_logout_url | default(None) }}" + extension_scripts: "{{ openshift_master_extension_scripts | default(None) }}" + extension_stylesheets: "{{ openshift_master_extension_stylesheets | default(None) }}" + extensions: "{{ openshift_master_extensions | default(None) }}" + oauth_template: "{{ openshift_master_oauth_template | default(None) }}" + etcd_hosts: "{{ openshift_master_etcd_hosts | default(None) }}" + etcd_port: "{{ openshift_master_etcd_port | default(None) }}" + etcd_use_ssl: "{{ openshift_master_etcd_use_ssl | default(None) }}" + etcd_urls: "{{ openshift_master_etcd_urls | default(None) }}" + embedded_etcd: "{{ openshift_master_embedded_etcd | default(None) }}" + embedded_kube: "{{ openshift_master_embedded_kube | default(None) }}" + embedded_dns: "{{ openshift_master_embedded_dns | default(None) }}" + dns_port: "{{ openshift_master_dns_port | default(None) }}" + bind_addr: "{{ openshift_master_bind_addr | default(None) }}" + pod_eviction_timeout: "{{ openshift_master_pod_eviction_timeout | default(None) }}" + portal_net: "{{ openshift_master_portal_net | default(None) }}" + session_max_seconds: "{{ openshift_master_session_max_seconds | default(None) }}" + session_name: "{{ openshift_master_session_name | default(None) }}" + session_secrets_file: "{{ openshift_master_session_secrets_file | default(None) }}" + session_auth_secrets: "{{ openshift_master_session_auth_secrets | default(None) }}" + session_encryption_secrets: "{{ openshift_master_session_encryption_secrets | default(None) }}" + access_token_max_seconds: "{{ openshift_master_access_token_max_seconds | default(None) }}" + auth_token_max_seconds: "{{ openshift_master_auth_token_max_seconds | default(None) }}" + identity_providers: "{{ openshift_master_identity_providers | default(None) }}" + registry_url: "{{ oreg_url | default(None) }}" + oauth_grant_method: "{{ openshift_master_oauth_grant_method | default(None) }}" + sdn_cluster_network_cidr: "{{ osm_cluster_network_cidr | default(None) }}" + sdn_host_subnet_length: "{{ osm_host_subnet_length | default(None) }}" + default_subdomain: "{{ openshift_master_default_subdomain | default(osm_default_subdomain) | default(None) }}" + custom_cors_origins: "{{ osm_custom_cors_origins | default(None) }}" + default_node_selector: "{{ osm_default_node_selector | default(None) }}" + project_request_message: "{{ osm_project_request_message | default(None) }}" + project_request_template: "{{ osm_project_request_template | default(None) }}" + mcs_allocator_range: "{{ osm_mcs_allocator_range | default(None) }}" + mcs_labels_per_project: "{{ osm_mcs_labels_per_project | default(None) }}" + uid_allocator_range: "{{ osm_uid_allocator_range | default(None) }}" + router_selector: "{{ openshift_router_selector | default(None) }}" + registry_selector: "{{ openshift_registry_selector | default(None) }}" + api_server_args: "{{ osm_api_server_args | default(None) }}" + controller_args: "{{ osm_controller_args | default(None) }}" + infra_nodes: "{{ openshift_infra_nodes | default(None) }}" + disabled_features: "{{ osm_disabled_features | default(None) }}" + master_count: "{{ openshift_master_count | default(None) }}" + controller_lease_ttl: "{{ osm_controller_lease_ttl | default(None) }}" + master_image: "{{ osm_image | default(None) }}" + scheduler_predicates: "{{ openshift_master_scheduler_predicates | default(None) }}" + scheduler_priorities: "{{ openshift_master_scheduler_priorities | default(None) }}" diff --git a/roles/openshift_node/handlers/main.yml b/roles/openshift_node/handlers/main.yml index 3fec4b1dd..1a1dc8ede 100644 --- a/roles/openshift_node/handlers/main.yml +++ b/roles/openshift_node/handlers/main.yml @@ -3,8 +3,6 @@ service: name={{ openshift.common.service_type }}-node state=restarted when: not (node_service_status_changed | default(false) | bool) -- name: restart docker - service: name=docker state=restarted - - name: restart openvswitch service: name=openvswitch state=restarted + when: not (ovs_service_status_changed | default(false) | bool) diff --git a/roles/openshift_node/meta/main.yml b/roles/openshift_node/meta/main.yml index c92008a77..84ba9ac2e 100644 --- a/roles/openshift_node/meta/main.yml +++ b/roles/openshift_node/meta/main.yml @@ -12,4 +12,6 @@ galaxy_info: categories: - cloud dependencies: -- { role: openshift_common } +- role: openshift_docker +- role: openshift_cloud_provider +- role: openshift_common diff --git a/roles/openshift_node/tasks/main.yml b/roles/openshift_node/tasks/main.yml index 2d3960cd8..80b3e710d 100644 --- a/roles/openshift_node/tasks/main.yml +++ b/roles/openshift_node/tasks/main.yml @@ -11,10 +11,8 @@ with_items: - role: common local_facts: - hostname: "{{ openshift_hostname | default(none) }}" - public_hostname: "{{ openshift_public_hostname | default(none) }}" - deployment_type: "{{ openshift_deployment_type }}" # TODO: Replace this with a lookup or filter plugin. + # TODO: Move this to the node role dns_ip: "{{ openshift_dns_ip | default(openshift_master_cluster_vip | default(None if openshift.common.version_gte_3_1_or_1_1 | bool else openshift_node_first_master_ip | default(None, true), true), true) }}" @@ -25,7 +23,6 @@ iptables_sync_period: "{{ openshift_node_iptables_sync_period | default(None) }}" kubelet_args: "{{ openshift_node_kubelet_args | default(None) }}" labels: "{{ lookup('oo_option', 'openshift_node_labels') | default( openshift_node_labels | default(none), true) }}" - portal_net: "{{ openshift_master_portal_net | default(None) }}" registry_url: "{{ oreg_url | default(none) }}" schedulable: "{{ openshift_schedulable | default(openshift_scheduleable) | default(None) }}" sdn_mtu: "{{ openshift_node_sdn_mtu | default(None) }}" @@ -34,6 +31,7 @@ node_image: "{{ osn_image | default(None) }}" ovs_image: "{{ osn_ovs_image | default(None) }}" proxy_mode: "{{ openshift_node_proxy_mode | default('iptables') }}" + local_quota_per_fsgroup: "{{ openshift_node_local_quota_per_fsgroup | default(None) }}" # We have to add tuned-profiles in the same transaction otherwise we run into depsolving # problems because the rpms don't pin the version properly. This was fixed in 3.1 packaging. @@ -55,36 +53,21 @@ docker pull {{ openshift.node.ovs_image }}:{{ openshift_version }} when: openshift.common.is_containerized | bool and openshift.common.use_openshift_sdn | bool -- name: Install Node docker service file - template: - dest: "/etc/systemd/system/{{ openshift.common.service_type }}-node.service" - src: openshift.docker.node.service - register: install_node_result - when: openshift.common.is_containerized | bool - -- name: Create the openvswitch service env file - template: - src: openvswitch.sysconfig.j2 - dest: /etc/sysconfig/openvswitch - when: openshift.common.is_containerized | bool - register: install_ovs_sysconfig - -- name: Install OpenvSwitch docker service file - template: - dest: "/etc/systemd/system/openvswitch.service" - src: openvswitch.docker.service - when: openshift.common.is_containerized | bool and openshift.common.use_openshift_sdn | bool - notify: - - restart openvswitch +- name: Install the systemd units + include: systemd_units.yml - name: Reload systemd units command: systemctl daemon-reload - when: openshift.common.is_containerized and ( ( install_node_result | changed ) + when: openshift.common.is_containerized | bool and ( ( install_node_result | changed ) or ( install_ovs_sysconfig | changed ) ) - name: Start and enable openvswitch docker service service: name=openvswitch.service enabled=yes state=started when: openshift.common.is_containerized | bool and openshift.common.use_openshift_sdn | bool + register: ovs_start_result + +- set_fact: + ovs_service_status_changed: "{{ ovs_start_result | changed }}" # TODO: add the validate parameter when there is a validation command to run - name: Create the Node config @@ -98,19 +81,18 @@ notify: - restart node -- name: Configure Node settings +- name: Configure AWS Cloud Provider Settings lineinfile: dest: /etc/sysconfig/{{ openshift.common.service_type }}-node regexp: "{{ item.regex }}" line: "{{ item.line }}" create: true with_items: - - regex: '^OPTIONS=' - line: "OPTIONS=--loglevel={{ openshift.node.debug_level }}" - - regex: '^CONFIG_FILE=' - line: "CONFIG_FILE={{ openshift_node_config_file }}" - - regex: '^IMAGE_VERSION=' - line: "IMAGE_VERSION={{ openshift_version }}" + - regex: '^AWS_ACCESS_KEY_ID=' + line: "AWS_ACCESS_KEY_ID={{ openshift.cloudprovider.aws.access_key }}" + - regex: '^AWS_SECRET_ACCESS_KEY=' + line: "AWS_SECRET_ACCESS_KEY={{ openshift.cloudprovider.aws.secret_key }}" + when: "'cloudprovider' in openshift and 'aws' in openshift.cloudprovider and openshift.cloudprovider.kind == 'aws' and 'access_key' in openshift.cloudprovider.aws and 'secret_key' in openshift.cloudprovider.aws" notify: - restart node @@ -124,7 +106,8 @@ # Using curl here since the uri module requires python-httplib2 and # wait_for port doesn't provide health information. command: > - curl -k --silent {{ openshift_node_master_api_url }}/healthz/ready + curl --silent --cacert {{ openshift.common.config_base }}/node/ca.crt + {{ openshift_node_master_api_url }}/healthz/ready register: api_available_output until: api_available_output.stdout == 'ok' retries: 120 @@ -134,7 +117,7 @@ - name: Start and enable node service: name={{ openshift.common.service_type }}-node enabled=yes state=started - register: start_result + register: node_start_result - set_fact: - node_service_status_changed: "{{ start_result | changed }}" + node_service_status_changed: "{{ node_start_result | changed }}" diff --git a/roles/openshift_node/tasks/systemd_units.yml b/roles/openshift_node/tasks/systemd_units.yml new file mode 100644 index 000000000..be4b4ed61 --- /dev/null +++ b/roles/openshift_node/tasks/systemd_units.yml @@ -0,0 +1,40 @@ +# This file is included both in the openshift_master role and in the upgrade +# playbooks. + +- name: Install Node docker service file + template: + dest: "/etc/systemd/system/{{ openshift.common.service_type }}-node.service" + src: openshift.docker.node.service + register: install_node_result + when: openshift.common.is_containerized | bool + +- name: Create the openvswitch service env file + template: + src: openvswitch.sysconfig.j2 + dest: /etc/sysconfig/openvswitch + when: openshift.common.is_containerized | bool + register: install_ovs_sysconfig + +- name: Install OpenvSwitch docker service file + template: + dest: "/etc/systemd/system/openvswitch.service" + src: openvswitch.docker.service + when: openshift.common.is_containerized | bool and openshift.common.use_openshift_sdn | bool + notify: + - restart openvswitch + +- name: Configure Node settings + lineinfile: + dest: /etc/sysconfig/{{ openshift.common.service_type }}-node + regexp: "{{ item.regex }}" + line: "{{ item.line }}" + create: true + with_items: + - regex: '^OPTIONS=' + line: "OPTIONS=--loglevel={{ openshift.node.debug_level }}" + - regex: '^CONFIG_FILE=' + line: "CONFIG_FILE={{ openshift_node_config_file }}" + - regex: '^IMAGE_VERSION=' + line: "IMAGE_VERSION={{ openshift_version }}" + notify: + - restart node diff --git a/roles/openshift_node/templates/node.yaml.v1.j2 b/roles/openshift_node/templates/node.yaml.v1.j2 index 67975d372..28cb1ea26 100644 --- a/roles/openshift_node/templates/node.yaml.v1.j2 +++ b/roles/openshift_node/templates/node.yaml.v1.j2 @@ -38,3 +38,6 @@ volumeDirectory: {{ openshift.common.data_dir }}/openshift.local.volumes proxyArguments: proxy-mode: - {{ openshift.node.proxy_mode }} +volumeConfig: + localQuota: + perFSGroup: {{ openshift.node.local_quota_per_fsgroup }} diff --git a/roles/openshift_node/templates/openshift.docker.node.service b/roles/openshift_node/templates/openshift.docker.node.service index 83b697f73..a8accca47 100644 --- a/roles/openshift_node/templates/openshift.docker.node.service +++ b/roles/openshift_node/templates/openshift.docker.node.service @@ -1,20 +1,22 @@ [Unit] -After=docker.service After={{ openshift.common.service_type }}-master.service +After=docker.service After=openvswitch.service +PartOf=docker.service +Requires=docker.service {% if openshift.common.use_openshift_sdn %} Requires=openvswitch.service {% endif %} -Requires=docker.service -PartOf=docker.service +Wants={{ openshift.common.service_type }}-master.service [Service] EnvironmentFile=/etc/sysconfig/{{ openshift.common.service_type }}-node ExecStartPre=-/usr/bin/docker rm -f {{ openshift.common.service_type }}-node -ExecStart=/usr/bin/docker run --name {{ openshift.common.service_type }}-node --rm --privileged --net=host --pid=host -v /:/rootfs:ro -e CONFIG_FILE=${CONFIG_FILE} -e OPTIONS=${OPTIONS} -e HOST=/rootfs -e HOST_ETC=/host-etc -v {{ openshift.common.data_dir }}:{{ openshift.common.data_dir }} -v {{ openshift.common.config_base }}/node:{{ openshift.common.config_base }}/node -v /etc/localtime:/etc/localtime:ro -v /etc/machine-id:/etc/machine-id:ro -v /run:/run -v /sys:/sys:ro -v /usr/bin/docker:/usr/bin/docker:ro -v /var/lib/docker:/var/lib/docker -v /lib/modules:/lib/modules -v /etc/origin/openvswitch:/etc/openvswitch -v /etc/origin/sdn:/etc/openshift-sdn -v /etc/systemd/system:/host-etc/systemd/system -v /var/log:/var/log {{ openshift.node.node_image }}:${IMAGE_VERSION} +ExecStart=/usr/bin/docker run --name {{ openshift.common.service_type }}-node --rm --privileged --net=host --pid=host --env-file=/etc/sysconfig/{{ openshift.common.service_type }}-node -v /:/rootfs:ro -e CONFIG_FILE=${CONFIG_FILE} -e OPTIONS=${OPTIONS} -e HOST=/rootfs -e HOST_ETC=/host-etc -v {{ openshift.common.data_dir }}:{{ openshift.common.data_dir }} -v {{ openshift.common.config_base }}/node:{{ openshift.common.config_base }}/node -v /etc/localtime:/etc/localtime:ro -v /etc/machine-id:/etc/machine-id:ro -v /run:/run -v /sys:/sys:ro -v /usr/bin/docker:/usr/bin/docker:ro -v /var/lib/docker:/var/lib/docker -v /lib/modules:/lib/modules -v /etc/origin/openvswitch:/etc/openvswitch -v /etc/origin/sdn:/etc/openshift-sdn -v /etc/systemd/system:/host-etc/systemd/system -v /var/log:/var/log -v /dev:/dev {{ openshift.node.node_image }}:${IMAGE_VERSION} ExecStartPost=/usr/bin/sleep 10 ExecStop=/usr/bin/docker stop {{ openshift.common.service_type }}-node +SyslogIdentifier={{ openshift.common.service_type }}-node Restart=always [Install] -WantedBy=multi-user.target +WantedBy=docker.service diff --git a/roles/openshift_node/templates/openvswitch.docker.service b/roles/openshift_node/templates/openvswitch.docker.service index 7af2b7431..8052a3a39 100644 --- a/roles/openshift_node/templates/openvswitch.docker.service +++ b/roles/openshift_node/templates/openvswitch.docker.service @@ -9,7 +9,8 @@ ExecStartPre=-/usr/bin/docker rm -f openvswitch ExecStart=/usr/bin/docker run --name openvswitch --rm --privileged --net=host --pid=host -v /lib/modules:/lib/modules -v /run:/run -v /sys:/sys:ro -v /etc/origin/openvswitch:/etc/openvswitch {{ openshift.node.ovs_image }}:${IMAGE_VERSION} ExecStartPost=/usr/bin/sleep 5 ExecStop=/usr/bin/docker stop openvswitch +SyslogIdentifier=openvswitch Restart=always [Install] -WantedBy=multi-user.target +WantedBy=docker.service diff --git a/roles/openshift_serviceaccounts/tasks/main.yml b/roles/openshift_serviceaccounts/tasks/main.yml index f34fa7b74..5dd28d52a 100644 --- a/roles/openshift_serviceaccounts/tasks/main.yml +++ b/roles/openshift_serviceaccounts/tasks/main.yml @@ -9,7 +9,8 @@ - name: create the service account shell: > echo {{ lookup('template', '../templates/serviceaccount.j2') - | from_yaml | to_json | quote }} | {{ openshift.common.client_binary }} create -f - + | from_yaml | to_json | quote }} | {{ openshift.common.client_binary }} + -n {{ openshift_serviceaccounts_namespace }} create -f - when: item.1.rc != 0 with_together: - openshift_serviceaccounts_names diff --git a/roles/openshift_storage_nfs/tasks/main.yml b/roles/openshift_storage_nfs/tasks/main.yml index fdd7bd3f1..fe7f83cbb 100644 --- a/roles/openshift_storage_nfs/tasks/main.yml +++ b/roles/openshift_storage_nfs/tasks/main.yml @@ -1,8 +1,6 @@ --- - name: Install nfs-utils - yum: - pkg: nfs-utils - state: present + action: "{{ ansible_pkg_mgr }} name=nfs-utils state=present" - name: Configure NFS lineinfile: diff --git a/roles/os_firewall/meta/main.yml b/roles/os_firewall/meta/main.yml index 8592371e8..c93335b7b 100644 --- a/roles/os_firewall/meta/main.yml +++ b/roles/os_firewall/meta/main.yml @@ -11,4 +11,5 @@ galaxy_info: - 7 categories: - system -dependencies: [] +dependencies: +- { role: openshift_facts } diff --git a/roles/os_zabbix/vars/template_heartbeat.yml b/roles/os_zabbix/vars/template_heartbeat.yml index 8dbe0d0d6..ec953c79b 100644 --- a/roles/os_zabbix/vars/template_heartbeat.yml +++ b/roles/os_zabbix/vars/template_heartbeat.yml @@ -11,3 +11,8 @@ g_template_heartbeat: expression: '{Template Heartbeat:heartbeat.ping.nodata(20m)}=1' priority: avg url: 'https://github.com/openshift/ops-sop/blob/master/V3/Alerts/check_node_heartbeat.asciidoc' + + - name: 'Heartbeat.ping has failed (60 min) on {HOST.NAME}' + expression: '{Template Heartbeat:heartbeat.ping.nodata(60m)}=1' + priority: high + url: 'https://github.com/openshift/ops-sop/blob/master/V3/Alerts/check_node_heartbeat.asciidoc' diff --git a/roles/os_zabbix/vars/template_openshift_master.yml b/roles/os_zabbix/vars/template_openshift_master.yml index e36f23a2b..a38db9f65 100644 --- a/roles/os_zabbix/vars/template_openshift_master.yml +++ b/roles/os_zabbix/vars/template_openshift_master.yml @@ -6,261 +6,314 @@ g_template_openshift_master: applications: - Openshift Master key: openshift.master.app.create + + - key: openshift.master.app.build.create + description: "check the app create with a build process" + value_type: int + applications: + - Openshift Master + + - key: openshift.master.app.create.time + description: "check the time it takes app create with a build process" + value_type: float + applications: + - Openshift Master + + - key: openshift.master.app.build.time + description: "check the time it takes app build" + value_type: float + applications: + - Openshift Master - key: openshift.master.process.count description: Shows number of master processes running - type: int + value_type: int applications: - Openshift Master - key: openshift.master.api.ping description: "Verify that the Openshift API is up (uses the cluster API URL)" - type: int + value_type: int applications: - Openshift Master - key: openshift.master.local.api.ping description: "Verify that the Openshift API is up on the host (uses the API URL as the https://127.0.0.1)" - type: int + value_type: int applications: - Openshift Master - key: openshift.master.api.healthz description: "Checks the healthz check of the master's api: https://<cluster_api_url>/healthz" - type: int + value_type: int data_type: bool applications: - Openshift Master - key: openshift.master.local.api.healthz description: "Checks the healthz check of the master's api: https://127.0.0.1/healthz" - type: int + value_type: int data_type: bool applications: - Openshift Master - key: openshift.master.user.count description: Shows number of users in a cluster - type: int + value_type: int applications: - Openshift Master - key: openshift.master.pod.running.count description: Shows number of pods running - type: int + value_type: int applications: - Openshift Master - key: openshift.master.pod.user.running.count description: Shows number of user pods running (non infrastructure pods) - type: int + value_type: int applications: - Openshift Master - key: openshift.master.pod.total.count description: Shows total number of pods (running and non running) - type: int + value_type: int applications: - Openshift Master - key: openshift.master.node.count description: Shows the total number of nodes found in the Openshift Cluster - type: int + value_type: int applications: - Openshift Master - key: openshift.project.count description: Shows number of projects on a cluster - type: int + value_type: int + applications: + - Openshift Master + + - key: openshift.master.pv.space.total + description: Shows the total space of pv + value_type: int + applications: + - Openshift Master + + - key: openshift.master.pv.space.available + description: Shows the available space of pv + value_type: int applications: - Openshift Master - key: openshift.master.pv.total.count description: Total number of Persistent Volumes in the Openshift Cluster - type: int + value_type: int applications: - Openshift Master - key: openshift.master.pv.available.count description: Total number of Available Persistent Volumes in the Openshift Cluster - type: int + value_type: int applications: - Openshift Master - key: openshift.master.pv.released.count description: Total number of Released Persistent Volumes in the Openshift Cluster - type: int + value_type: int applications: - Openshift Master - key: openshift.master.pv.bound.count description: Total number of Bound Persistent Volumes in the Openshift Cluster - type: int + value_type: int applications: - Openshift Master - key: openshift.master.pv.failed.count description: Total number of Failed Persistent Volumes in the Openshift Cluster - type: int + value_type: int applications: - Openshift Master - key: openshift.master.skydns.port.open description: State of the SkyDNS port open and listening - type: int + value_type: int applications: - Openshift Master - key: openshift.master.skydns.query description: SkyDNS can be queried or not - type: int + value_type: int applications: - Openshift Master - key: openshift.master.etcd.create.success description: Show number of successful create actions - type: int + value_type: int applications: - Openshift Etcd - key: openshift.master.etcd.create.fail description: Show number of failed create actions - type: int + value_type: int applications: - Openshift Etcd - key: openshift.master.etcd.delete.success description: Show number of successful delete actions - type: int + value_type: int applications: - Openshift Etcd - key: openshift.master.etcd.delete.fail description: Show number of failed delete actions - type: int + value_type: int applications: - Openshift Etcd - key: openshift.master.etcd.get.success description: Show number of successful get actions - type: int + value_type: int applications: - Openshift Etcd - key: openshift.master.etcd.get.fail description: Show number of failed get actions - type: int + value_type: int applications: - Openshift Etcd - key: openshift.master.etcd.set.success description: Show number of successful set actions - type: int + value_type: int applications: - Openshift Etcd - key: openshift.master.etcd.set.fail description: Show number of failed set actions - type: int + value_type: int applications: - Openshift Etcd - key: openshift.master.etcd.update.success description: Show number of successful update actions - type: int + value_type: int applications: - Openshift Etcd - key: openshift.master.etcd.update.fail description: Show number of failed update actions - type: int + value_type: int applications: - Openshift Etcd - key: openshift.master.etcd.watchers description: Show number of etcd watchers - type: int + value_type: int applications: - Openshift Etcd - key: openshift.master.etcd.ping description: etcd ping - type: int + value_type: int applications: - Openshift Etcd - key: openshift.master.metric.ping description: "This check verifies that the https://master/metrics check is alive and communicating properly." - type: int + value_type: int applications: - Openshift Master Metrics - key: openshift.master.nodesnotready.count description: "This check shows how many nodes in a cluster are in NotReady state." - type: int + value_type: int applications: - Openshift Master - key: openshift.master.nodesnotschedulable.count description: "This check shows how many nodes in a cluster are not schedulable." - type: int + value_type: int applications: - Openshift Master - key: openshift.master.apiserver.latency.summary.pods.quantile.list.5 description: "Value from https://master/metrics. This is the time, in miliseconds, that 50% of the pod operations have taken to completed." - type: int + value_type: int applications: - Openshift Master Metrics - key: openshift.master.apiserver.latency.summary.pods.quantile.list.9 description: "Value from https://master/metrics. This is the time, in miliseconds, that 90% of the pod operations have taken to completed." - type: int + value_type: int applications: - Openshift Master Metrics - key: openshift.master.apiserver.latency.summary.pods.quantile.list.99 description: "Value from https://master/metrics. This is the time, in miliseconds, that 99% of the pod operations have taken to completed." - type: int + value_type: int applications: - Openshift Master Metrics - key: openshift.master.apiserver.latency.summary.pods.quantile.watchlist.5 description: "Value from https://master/metrics. This is the time, in miliseconds, that 50% of the pod operations have taken to completed." - type: int + value_type: int applications: - Openshift Master Metrics - key: openshift.master.apiserver.latency.summary.pods.quantile.watchlist.9 description: "Value from https://master/metrics. This is the time, in miliseconds, that 90% of the pod operations have taken to completed." - type: int + value_type: int applications: - Openshift Master Metrics - key: openshift.master.apiserver.latency.summary.pods.quantile.watchlist.99 description: "Value from https://master/metrics. This is the time, in miliseconds, that 99% of the pod operations have taken to completed." - type: int + value_type: int applications: - Openshift Master Metrics - key: openshift.master.scheduler.e2e.scheduling.latency.quantile.5 description: "Value from https://master/metrics. This is the time, in miliseconds, that 50% of the end to end scheduling operations have taken to completed." - type: int + value_type: int applications: - Openshift Master Metrics - key: openshift.master.scheduler.e2e.scheduling.latency.quantile.9 description: "Value from https://master/metrics. This is the time, in miliseconds, that 90% of the end to end scheduling operations have taken to completed." - type: int + value_type: int applications: - Openshift Master Metrics - key: openshift.master.scheduler.e2e.scheduling.latency.quantile.99 description: "Value from https://master/metrics. This is the time, in miliseconds, that 99% of the end to end scheduling operations have taken to completed." - type: int + value_type: int applications: - Openshift Master Metrics + zdiscoveryrules: + - name: disc.pv + key: disc.pv + lifetime: 1 + description: "Dynamically register the Persistent Volumes" + + zitemprototypes: + - discoveryrule_key: disc.pv + name: "disc.pv.count.{#OSO_PV}" + key: "disc.pv.count[{#OSO_PV}]" + value_type: int + description: "Number of PV's of this size" + applications: + - Openshift Master + + - discoveryrule_key: disc.pv + name: "disc.pv.available.{#OSO_PV}" + key: "disc.pv.available[{#OSO_PV}]" + value_type: int + description: "Number of PV's of this size that are available" + applications: + - Openshift Master + ztriggers: - name: 'Openshift Master process not running on {HOST.NAME}' expression: '{Template Openshift Master:openshift.master.process.count.max(#3)}<1' @@ -295,6 +348,13 @@ g_template_openshift_master: - 'Openshift Master process not running on {HOST.NAME}' priority: avg + - name: 'Application creation with build has failed on {HOST.NAME}' + expression: '{Template Openshift Master:openshift.master.app.build.create.last(#1)}=1 and {Template Openshift Master:openshift.master.app.build.create.last(#2)}=1' + url: 'https://github.com/openshift/ops-sop/blob/master/V3/Alerts/check_create_app.asciidoc' + dependencies: + - 'Openshift Master process not running on {HOST.NAME}' + priority: avg + - name: 'Application creation has failed multiple times in the last hour on {HOST.NAME}' expression: '{Template Openshift Master:openshift.master.app.create.sum(1h)}>3' url: 'https://github.com/openshift/ops-sop/blob/master/V3/Alerts/check_create_app.asciidoc' @@ -303,6 +363,14 @@ g_template_openshift_master: description: The application create loop has failed 4 or more times in the last hour priority: avg + - name: 'Application with build creation has failed multiple times in the last 2 hour on {HOST.NAME}' + expression: '{Template Openshift Master:openshift.master.app.build.create.sum(2h)}>3' + url: 'https://github.com/openshift/ops-sop/blob/master/V3/Alerts/check_create_app.asciidoc' + dependencies: + - 'Openshift Master process not running on {HOST.NAME}' + description: The application create loop has failed 4 or more times in the last hour + priority: avg + - name: 'Openshift Master API health check is failing on {HOST.NAME}' expression: '{Template Openshift Master:openshift.master.api.healthz.max(#3)}<1' url: 'https://github.com/openshift/ops-sop/blob/master/V3/Alerts/openshift_master.asciidoc' diff --git a/roles/os_zabbix/vars/template_openshift_node.yml b/roles/os_zabbix/vars/template_openshift_node.yml index 66bd3a147..9f84a2cdf 100644 --- a/roles/os_zabbix/vars/template_openshift_node.yml +++ b/roles/os_zabbix/vars/template_openshift_node.yml @@ -4,37 +4,37 @@ g_template_openshift_node: zitems: - key: openshift.node.process.count description: Shows number of OpenShift Node processes running - type: int + value_type: int applications: - Openshift Node - key: openshift.node.ovs.pids.count description: Shows number of ovs process ids running - type: int + value_type: int applications: - Openshift Node - key: openshift.node.ovs.ports.count description: Shows number of OVS ports defined - type: int + value_type: int applications: - Openshift Node - key: openshift.node.ovs.stray.rules description: Number of OVS stray rules found/removed - type: int + value_type: int applications: - Openshift Node - key: openshift.node.registry-pods.healthy_pct description: Shows the percentage of healthy registries in the cluster - type: int + value_type: int applications: - Openshift Node - key: openshift.node.registry.service.ping description: Ping docker-registry service from node - type: int + value_type: int applications: - Openshift Node diff --git a/test/env-setup b/test/env-setup index 156593571..7456a641b 100644 --- a/test/env-setup +++ b/test/env-setup @@ -2,7 +2,7 @@ CUR_PATH=$(pwd) -PREFIX_PYTHONPATH=$CUR_PATH/inventory/ +PREFIX_PYTHONPATH=$CUR_PATH/inventory/:$CUR_PATH/roles/lib_yaml_editor/library export PYTHONPATH=$PREFIX_PYTHONPATH:$PYTHONPATH diff --git a/test/units/yedit_test.py b/test/units/yedit_test.py new file mode 100755 index 000000000..09a65e888 --- /dev/null +++ b/test/units/yedit_test.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python2 +''' + Unit tests for yedit +''' + +import unittest +import os + +# Removing invalid variable names for tests so that I can +# keep them brief +# pylint: disable=invalid-name,no-name-in-module +from yedit import Yedit + +class YeditTest(unittest.TestCase): + ''' + Test class for yedit + ''' + data = {'a': 'a', + 'b': {'c': {'d': [{'e': 'x'}, 'f', 'g']}}, + } + + filename = 'yedit_test.yml' + + def setUp(self): + ''' setup method will create a file and set to known configuration ''' + yed = Yedit(YeditTest.filename) + yed.yaml_dict = YeditTest.data + yed.write() + + def test_load(self): + ''' Testing a get ''' + yed = Yedit('yedit_test.yml') + self.assertEqual(yed.yaml_dict, self.data) + + def test_write(self): + ''' Testing a simple write ''' + yed = Yedit('yedit_test.yml') + yed.put('key1', 1) + yed.write() + self.assertTrue(yed.yaml_dict.has_key('key1')) + self.assertEqual(yed.yaml_dict['key1'], 1) + + def test_write_x_y_z(self): + '''Testing a write of multilayer key''' + yed = Yedit('yedit_test.yml') + yed.put('x.y.z', 'modified') + yed.write() + yed.load() + self.assertEqual(yed.get('x.y.z'), 'modified') + + def test_delete_a(self): + '''Testing a simple delete ''' + yed = Yedit('yedit_test.yml') + yed.delete('a') + yed.write() + yed.load() + self.assertTrue(not yed.yaml_dict.has_key('a')) + + def test_delete_b_c(self): + '''Testing delete of layered key ''' + yed = Yedit('yedit_test.yml') + yed.delete('b.c') + yed.write() + yed.load() + self.assertTrue(yed.yaml_dict.has_key('b')) + self.assertFalse(yed.yaml_dict['b'].has_key('c')) + + def test_create(self): + '''Testing a create ''' + os.unlink(YeditTest.filename) + yed = Yedit('yedit_test.yml') + yed.create('foo', 'bar') + yed.write() + yed.load() + self.assertTrue(yed.yaml_dict.has_key('foo')) + self.assertTrue(yed.yaml_dict['foo'] == 'bar') + + def test_create_content(self): + '''Testing a create with content ''' + content = {"foo": "bar"} + yed = Yedit("yedit_test.yml", content) + yed.write() + yed.load() + self.assertTrue(yed.yaml_dict.has_key('foo')) + self.assertTrue(yed.yaml_dict['foo'], 'bar') + + def test_array_insert(self): + '''Testing a create with content ''' + yed = Yedit("yedit_test.yml") + yed.put('b.c.d[0]', 'inject') + self.assertTrue(yed.get('b.c.d[0]') == 'inject') + + def test_array_insert_first_index(self): + '''Testing a create with content ''' + yed = Yedit("yedit_test.yml") + yed.put('b.c.d[0]', 'inject') + self.assertTrue(yed.get('b.c.d[1]') == 'f') + + def test_array_insert_second_index(self): + '''Testing a create with content ''' + yed = Yedit("yedit_test.yml") + yed.put('b.c.d[0]', 'inject') + self.assertTrue(yed.get('b.c.d[2]') == 'g') + + def test_dict_array_dict_access(self): + '''Testing a create with content''' + yed = Yedit("yedit_test.yml") + yed.put('b.c.d[0]', [{'x': {'y': 'inject'}}]) + self.assertTrue(yed.get('b.c.d[0].[0].x.y') == 'inject') + + def test_dict_array_dict_replace(self): + '''Testing multilevel delete''' + yed = Yedit("yedit_test.yml") + yed.put('b.c.d[0]', [{'x': {'y': 'inject'}}]) + yed.put('b.c.d[0].[0].x.y', 'testing') + self.assertTrue(yed.yaml_dict.has_key('b')) + self.assertTrue(yed.yaml_dict['b'].has_key('c')) + self.assertTrue(yed.yaml_dict['b']['c'].has_key('d')) + self.assertTrue(isinstance(yed.yaml_dict['b']['c']['d'], list)) + self.assertTrue(isinstance(yed.yaml_dict['b']['c']['d'][0], list)) + self.assertTrue(isinstance(yed.yaml_dict['b']['c']['d'][0][0], dict)) + self.assertTrue(yed.yaml_dict['b']['c']['d'][0][0]['x'].has_key('y')) + self.assertTrue(yed.yaml_dict['b']['c']['d'][0][0]['x']['y'], 'testing') + + def test_dict_array_dict_remove(self): + '''Testing multilevel delete''' + yed = Yedit("yedit_test.yml") + yed.put('b.c.d[0]', [{'x': {'y': 'inject'}}]) + yed.delete('b.c.d[0].[0].x.y') + self.assertTrue(yed.yaml_dict.has_key('b')) + self.assertTrue(yed.yaml_dict['b'].has_key('c')) + self.assertTrue(yed.yaml_dict['b']['c'].has_key('d')) + self.assertTrue(isinstance(yed.yaml_dict['b']['c']['d'], list)) + self.assertTrue(isinstance(yed.yaml_dict['b']['c']['d'][0], list)) + self.assertTrue(isinstance(yed.yaml_dict['b']['c']['d'][0][0], dict)) + self.assertFalse(yed.yaml_dict['b']['c']['d'][0][0]['x'].has_key('y')) + + def tearDown(self): + '''TearDown method''' + os.unlink(YeditTest.filename) + +if __name__ == "__main__": + unittest.main() diff --git a/utils/src/ooinstall/cli_installer.py b/utils/src/ooinstall/cli_installer.py index c53ca7b18..50a688eca 100644 --- a/utils/src/ooinstall/cli_installer.py +++ b/utils/src/ooinstall/cli_installer.py @@ -666,7 +666,7 @@ def get_hosts_to_run_on(oo_cfg, callback_facts, unattended, force, verbose): openshift_ansible.set_config(oo_cfg) click.echo('Gathering information from hosts...') callback_facts, error = openshift_ansible.default_facts(oo_cfg.hosts, verbose) - if error: + if error or callback_facts is None: click.echo("There was a problem fetching the required information. See " \ "{} for details.".format(oo_cfg.settings['ansible_log_path'])) sys.exit(1) @@ -780,42 +780,67 @@ def uninstall(ctx): @click.command() +@click.option('--latest-minor', '-l', is_flag=True, default=False) +@click.option('--next-major', '-n', is_flag=True, default=False) @click.pass_context -def upgrade(ctx): +def upgrade(ctx, latest_minor, next_major): oo_cfg = ctx.obj['oo_cfg'] verbose = ctx.obj['verbose'] + upgrade_mappings = { + '3.0':{ + 'minor_version' :'3.0', + 'minor_playbook':'v3_0_minor/upgrade.yml', + 'major_version' :'3.1', + 'major_playbook':'v3_0_to_v3_1/upgrade.yml', + }, + '3.1':{ + 'minor_version' :'3.1', + 'minor_playbook':'v3_1_minor/upgrade.yml', + 'major_playbook':'v3_1_to_v3_2/upgrade.yml', + 'major_version' :'3.2', + } + } + if len(oo_cfg.hosts) == 0: click.echo("No hosts defined in: %s" % oo_cfg.config_path) sys.exit(1) old_variant = oo_cfg.settings['variant'] old_version = oo_cfg.settings['variant_version'] - + mapping = upgrade_mappings.get(old_version) message = """ This tool will help you upgrade your existing OpenShift installation. """ click.echo(message) - click.echo("Version {} found. Do you want to update to the latest version of {} " \ - "or migrate to the next major release?".format(old_version, old_version)) - resp = click.prompt("(1) Update to latest {} (2) Migrate to next relese".format(old_version)) - if resp == "2": - # TODO: Make this a lot more flexible - new_version = "3.1" + if not (latest_minor or next_major): + click.echo("Version {} found. Do you want to update to the latest version of {} " \ + "or migrate to the next major release?".format(old_version, old_version)) + response = click.prompt("(1) Update to latest {} " \ + "(2) Migrate to next release".format(old_version), + type=click.Choice(['1', '2']),) + if response == "1": + latest_minor = True + if response == "2": + next_major = True + + if next_major: + playbook = mapping['major_playbook'] + new_version = mapping['major_version'] # Update config to reflect the version we're targetting, we'll write # to disk once ansible completes successfully, not before. + oo_cfg.settings['variant_version'] = new_version if oo_cfg.settings['variant'] == 'enterprise': oo_cfg.settings['variant'] = 'openshift-enterprise' - version = find_variant(oo_cfg.settings['variant'])[1] - oo_cfg.settings['variant_version'] = version.name - else: - new_version = old_version + + if latest_minor: + playbook = mapping['minor_playbook'] + new_version = mapping['minor_version'] click.echo("Openshift will be upgraded from %s %s to %s %s on the following hosts:\n" % ( - old_variant, old_version, oo_cfg.settings['variant'], - oo_cfg.settings['variant_version'])) + old_variant, old_version, oo_cfg.settings['variant'], new_version)) for host in oo_cfg.hosts: click.echo(" * %s" % host.connect_to) @@ -826,7 +851,7 @@ def upgrade(ctx): click.echo("Upgrade cancelled.") sys.exit(0) - retcode = openshift_ansible.run_upgrade_playbook(old_version, new_version, verbose) + retcode = openshift_ansible.run_upgrade_playbook(playbook, verbose) if retcode > 0: click.echo("Errors encountered during upgrade, please check %s." % oo_cfg.settings['ansible_log_path']) @@ -853,7 +878,7 @@ def install(ctx, force): click.echo('Gathering information from hosts...') callback_facts, error = openshift_ansible.default_facts(oo_cfg.hosts, verbose) - if error: + if error or callback_facts is None: click.echo("There was a problem fetching the required information. " \ "Please see {} for details.".format(oo_cfg.settings['ansible_log_path'])) sys.exit(1) diff --git a/utils/src/ooinstall/openshift_ansible.py b/utils/src/ooinstall/openshift_ansible.py index 04cccf89d..6e43eac9f 100644 --- a/utils/src/ooinstall/openshift_ansible.py +++ b/utils/src/ooinstall/openshift_ansible.py @@ -38,10 +38,10 @@ def generate_inventory(hosts): base_inventory.write('deployment_type={}\n'.format(ver.ansible_key)) if 'OO_INSTALL_ADDITIONAL_REGISTRIES' in os.environ: - base_inventory.write('cli_docker_additional_registries={}\n' + base_inventory.write('openshift_docker_additional_registries={}\n' .format(os.environ['OO_INSTALL_ADDITIONAL_REGISTRIES'])) if 'OO_INSTALL_INSECURE_REGISTRIES' in os.environ: - base_inventory.write('cli_docker_insecure_registries={}\n' + base_inventory.write('openshift_docker_insecure_registries={}\n' .format(os.environ['OO_INSTALL_INSECURE_REGISTRIES'])) if 'OO_INSTALL_PUDDLE_REPO' in os.environ: # We have to double the '{' here for literals @@ -251,18 +251,10 @@ def run_uninstall_playbook(verbose=False): return run_ansible(playbook, inventory_file, facts_env, verbose) -def run_upgrade_playbook(old_version, new_version, verbose=False): - # TODO: do not hardcode the upgrade playbook, add ability to select the - # right playbook depending on the type of upgrade. - old_version = old_version.replace('.', '_') - new_version = old_version.replace('.', '_') - if old_version == new_version: - playbook = os.path.join(CFG.settings['ansible_playbook_directory'], - 'playbooks/byo/openshift-cluster/upgrades/v{}_minor/upgrade.yml'.format(new_version)) - else: - playbook = os.path.join(CFG.settings['ansible_playbook_directory'], - 'playbooks/byo/openshift-cluster/upgrades/v{}_to_v{}/upgrade.yml'.format(old_version, - new_version)) +def run_upgrade_playbook(playbook, verbose=False): + playbook = os.path.join(CFG.settings['ansible_playbook_directory'], + 'playbooks/byo/openshift-cluster/upgrades/{}'.format(playbook)) + # TODO: Upgrade inventory for upgrade? inventory_file = generate_inventory(CFG.hosts) facts_env = os.environ.copy() diff --git a/utils/src/ooinstall/variants.py b/utils/src/ooinstall/variants.py index 571025543..9d98379bb 100644 --- a/utils/src/ooinstall/variants.py +++ b/utils/src/ooinstall/variants.py @@ -36,6 +36,7 @@ class Variant(object): # WARNING: Keep the versions ordered, most recent last: OSE = Variant('openshift-enterprise', 'OpenShift Enterprise', [ + Version('3.2', 'openshift-enterprise'), Version('3.1', 'openshift-enterprise'), Version('3.0', 'enterprise') ] @@ -43,6 +44,7 @@ OSE = Variant('openshift-enterprise', 'OpenShift Enterprise', AEP = Variant('atomic-enterprise', 'Atomic Enterprise Platform', [ + Version('3.2', 'atomic-enterprise'), Version('3.1', 'atomic-enterprise') ] ) @@ -74,4 +76,3 @@ def get_variant_version_combos(): for ver in variant.versions: combos.append((variant, ver)) return combos - diff --git a/utils/test/cli_installer_tests.py b/utils/test/cli_installer_tests.py index 6ba5ec1eb..784a30830 100644 --- a/utils/test/cli_installer_tests.py +++ b/utils/test/cli_installer_tests.py @@ -480,7 +480,7 @@ class UnattendedCliTests(OOCliFixture): self.assertEquals('openshift-enterprise', written_config['variant']) # We didn't specify a version so the latest should have been assumed, # and written to disk: - self.assertEquals('3.1', written_config['variant_version']) + self.assertEquals('3.2', written_config['variant_version']) # Make sure the correct value was passed to ansible: inventory = ConfigParser.ConfigParser(allow_no_value=True) @@ -960,12 +960,13 @@ class AttendedCliTests(OOCliFixture): cli_input = build_input(hosts=[ ('10.0.0.1', True, False)], ssh_user='root', - variant_num=2, + variant_num=3, confirm_facts='y') self.cli_args.append("install") result = self.runner.invoke(cli.cli, self.cli_args, input=cli_input) self.assert_result(result, 0) + print result.output self.assertTrue("NOTE: Add a total of 3 or more Masters to perform an HA installation." not in result.output) |