summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.tito/packages/openshift-ansible2
-rwxr-xr-xbin/cluster15
-rw-r--r--filter_plugins/oo_filters.py17
-rw-r--r--filter_plugins/openshift_master.py13
-rw-r--r--inventory/byo/hosts.aep.example24
-rw-r--r--inventory/byo/hosts.origin.example24
-rw-r--r--inventory/byo/hosts.ose.example24
-rw-r--r--openshift-ansible.spec65
-rw-r--r--playbooks/adhoc/openshift_hosted_logging_efk.yaml6
-rw-r--r--playbooks/common/openshift-cluster/upgrades/files/ensure_system_units_have_version.sh51
-rw-r--r--playbooks/common/openshift-cluster/upgrades/v3_1_to_v3_2/containerized_upgrade.yml8
-rw-r--r--playbooks/common/openshift-cluster/upgrades/v3_1_to_v3_2/pre.yml43
-rw-r--r--playbooks/common/openshift-cluster/upgrades/v3_1_to_v3_2/upgrade.yml42
-rw-r--r--playbooks/common/openshift-etcd/config.yml2
-rw-r--r--playbooks/common/openshift-master/config.yml3
-rw-r--r--playbooks/openstack/openshift-cluster/files/heat_stack.yaml4
-rw-r--r--playbooks/openstack/openshift-cluster/files/user-data6
-rw-r--r--roles/etcd/tasks/main.yml3
-rw-r--r--roles/etcd/templates/etcd.docker.service2
-rw-r--r--roles/lib_openshift_api/build/ansible/edit.py78
-rw-r--r--roles/lib_openshift_api/build/ansible/obj.py132
-rw-r--r--roles/lib_openshift_api/build/ansible/secret.py121
-rwxr-xr-xroles/lib_openshift_api/build/generate.py59
-rw-r--r--roles/lib_openshift_api/build/src/base.py273
-rw-r--r--roles/lib_openshift_api/build/src/edit.py49
-rw-r--r--roles/lib_openshift_api/build/src/obj.py78
-rw-r--r--roles/lib_openshift_api/build/src/secret.py68
-rw-r--r--roles/lib_openshift_api/build/test/README5
-rwxr-xr-xroles/lib_openshift_api/build/test/deploymentconfig.yml120
-rwxr-xr-xroles/lib_openshift_api/build/test/edit.yml53
-rw-r--r--roles/lib_openshift_api/build/test/files/config.yml1
-rw-r--r--roles/lib_openshift_api/build/test/files/dc-mod.yml124
-rw-r--r--roles/lib_openshift_api/build/test/files/dc.yml120
-rw-r--r--roles/lib_openshift_api/build/test/files/passwords.yml4
-rw-r--r--roles/lib_openshift_api/build/test/files/router-mod.json30
-rw-r--r--roles/lib_openshift_api/build/test/files/router.json29
l---------roles/lib_openshift_api/build/test/roles1
-rwxr-xr-xroles/lib_openshift_api/build/test/secrets.yml81
-rwxr-xr-xroles/lib_openshift_api/build/test/services.yml133
-rw-r--r--roles/lib_openshift_api/library/oc_edit.py605
-rw-r--r--roles/lib_openshift_api/library/oc_obj.py688
-rw-r--r--roles/lib_openshift_api/library/oc_secret.py667
-rw-r--r--roles/lib_openshift_api/library/oc_secrets.py379
-rw-r--r--roles/lib_yaml_editor/build/ansible/yedit.py66
-rwxr-xr-xroles/lib_yaml_editor/build/generate.py43
-rw-r--r--roles/lib_yaml_editor/build/src/base.py17
-rw-r--r--roles/lib_yaml_editor/build/src/yedit.py201
-rw-r--r--roles/lib_yaml_editor/build/test/foo.yml1
-rwxr-xr-xroles/lib_yaml_editor/build/test/test.yaml15
-rw-r--r--roles/lib_yaml_editor/library/yedit.py183
-rw-r--r--roles/openshift_cli/meta/main.yml3
-rw-r--r--roles/openshift_cli/tasks/main.yml6
-rw-r--r--roles/openshift_cli_facts/meta/main.yml15
-rw-r--r--roles/openshift_cli_facts/tasks/main.yml6
-rw-r--r--roles/openshift_cloud_provider/defaults/main.yml7
-rw-r--r--roles/openshift_cloud_provider/meta/main.yml15
-rw-r--r--roles/openshift_cloud_provider/tasks/aws.yml6
-rw-r--r--roles/openshift_cloud_provider/tasks/main.yml23
-rw-r--r--roles/openshift_cloud_provider/tasks/openstack.yml9
-rw-r--r--roles/openshift_cloud_provider/templates/openstack.conf.j217
-rw-r--r--roles/openshift_cloud_provider/templates/openstack/cloud.conf.j217
-rw-r--r--roles/openshift_cloud_provider/vars/main.yml4
-rw-r--r--roles/openshift_common/meta/main.yml7
-rw-r--r--roles/openshift_common/tasks/main.yml6
-rw-r--r--roles/openshift_examples/files/examples/v1.1/db-templates/README.md2
-rw-r--r--roles/openshift_examples/files/examples/v1.1/db-templates/mongodb-ephemeral-template.json32
-rw-r--r--roles/openshift_examples/files/examples/v1.1/db-templates/mongodb-persistent-template.json36
-rw-r--r--roles/openshift_examples/files/examples/v1.1/db-templates/mysql-ephemeral-template.json30
-rw-r--r--roles/openshift_examples/files/examples/v1.1/db-templates/mysql-persistent-template.json34
-rw-r--r--roles/openshift_examples/files/examples/v1.1/db-templates/postgresql-ephemeral-template.json28
-rw-r--r--roles/openshift_examples/files/examples/v1.1/db-templates/postgresql-persistent-template.json32
-rw-r--r--roles/openshift_examples/files/examples/v1.1/quickstart-templates/cakephp-mysql.json10
-rw-r--r--roles/openshift_examples/files/examples/v1.1/quickstart-templates/cakephp.json8
-rw-r--r--roles/openshift_examples/files/examples/v1.1/quickstart-templates/dancer-mysql.json10
-rw-r--r--roles/openshift_examples/files/examples/v1.1/quickstart-templates/dancer.json8
-rw-r--r--roles/openshift_examples/files/examples/v1.1/quickstart-templates/django-postgresql.json10
-rw-r--r--roles/openshift_examples/files/examples/v1.1/quickstart-templates/django.json8
-rw-r--r--roles/openshift_examples/files/examples/v1.1/quickstart-templates/jenkins-ephemeral-template.json20
-rw-r--r--roles/openshift_examples/files/examples/v1.1/quickstart-templates/jenkins-persistent-template.json24
-rw-r--r--roles/openshift_examples/files/examples/v1.1/quickstart-templates/nodejs-mongodb.json10
-rw-r--r--roles/openshift_examples/files/examples/v1.1/quickstart-templates/nodejs.json8
-rw-r--r--roles/openshift_examples/files/examples/v1.1/quickstart-templates/rails-postgresql.json10
-rw-r--r--roles/openshift_examples/files/examples/v1.2/db-templates/README.md2
-rw-r--r--roles/openshift_examples/files/examples/v1.2/db-templates/mongodb-ephemeral-template.json32
-rw-r--r--roles/openshift_examples/files/examples/v1.2/db-templates/mongodb-persistent-template.json36
-rw-r--r--roles/openshift_examples/files/examples/v1.2/db-templates/mysql-ephemeral-template.json30
-rw-r--r--roles/openshift_examples/files/examples/v1.2/db-templates/mysql-persistent-template.json34
-rw-r--r--roles/openshift_examples/files/examples/v1.2/db-templates/postgresql-ephemeral-template.json28
-rw-r--r--roles/openshift_examples/files/examples/v1.2/db-templates/postgresql-persistent-template.json32
-rw-r--r--roles/openshift_examples/files/examples/v1.2/quickstart-templates/cakephp-mysql.json10
-rw-r--r--roles/openshift_examples/files/examples/v1.2/quickstart-templates/cakephp.json8
-rw-r--r--roles/openshift_examples/files/examples/v1.2/quickstart-templates/dancer-mysql.json10
-rw-r--r--roles/openshift_examples/files/examples/v1.2/quickstart-templates/dancer.json8
-rw-r--r--roles/openshift_examples/files/examples/v1.2/quickstart-templates/django-postgresql.json10
-rw-r--r--roles/openshift_examples/files/examples/v1.2/quickstart-templates/django.json8
-rw-r--r--roles/openshift_examples/files/examples/v1.2/quickstart-templates/jenkins-ephemeral-template.json20
-rw-r--r--roles/openshift_examples/files/examples/v1.2/quickstart-templates/jenkins-persistent-template.json24
-rw-r--r--roles/openshift_examples/files/examples/v1.2/quickstart-templates/nodejs-mongodb.json10
-rw-r--r--roles/openshift_examples/files/examples/v1.2/quickstart-templates/nodejs.json8
-rw-r--r--roles/openshift_examples/files/examples/v1.2/quickstart-templates/rails-postgresql.json10
-rwxr-xr-xroles/openshift_facts/library/openshift_facts.py267
-rw-r--r--roles/openshift_facts/tasks/main.yml2
-rw-r--r--roles/openshift_hosted_logging/README.md10
-rw-r--r--roles/openshift_hosted_logging/files/logging-deployer-sa.yaml6
-rw-r--r--roles/openshift_hosted_logging/meta/main.yaml3
-rw-r--r--roles/openshift_hosted_logging/tasks/cleanup_logging.yaml59
-rw-r--r--roles/openshift_hosted_logging/tasks/deploy_logging.yaml105
-rw-r--r--roles/openshift_hosted_logging/tasks/main.yaml8
-rw-r--r--roles/openshift_hosted_logging/vars/main.yaml6
-rw-r--r--roles/openshift_manageiq/tasks/main.yaml13
-rw-r--r--roles/openshift_manageiq/vars/main.yml8
-rw-r--r--roles/openshift_master/handlers/main.yml6
-rw-r--r--roles/openshift_master/meta/main.yml2
-rw-r--r--roles/openshift_master/tasks/main.yml126
-rw-r--r--roles/openshift_master/tasks/systemd_units.yml69
-rw-r--r--roles/openshift_master/templates/atomic-openshift-master.j25
-rw-r--r--roles/openshift_master/templates/docker-cluster/atomic-openshift-master-api.service.j22
-rw-r--r--roles/openshift_master/templates/docker-cluster/atomic-openshift-master-controllers.service.j22
-rw-r--r--roles/openshift_master/templates/docker/master.docker.service.j22
-rw-r--r--roles/openshift_master/templates/native-cluster/atomic-openshift-master-api.j25
-rw-r--r--roles/openshift_master/templates/native-cluster/atomic-openshift-master-controllers.j25
-rw-r--r--roles/openshift_master/templates/scheduler.json.j215
-rw-r--r--roles/openshift_master/vars/main.yml7
-rw-r--r--roles/openshift_master_facts/meta/main.yml15
-rw-r--r--roles/openshift_master_facts/tasks/main.yml67
-rw-r--r--roles/openshift_node/meta/main.yml3
-rw-r--r--roles/openshift_node/tasks/main.yml37
-rw-r--r--roles/openshift_node/tasks/systemd_units.yml40
-rw-r--r--roles/openshift_node/templates/node.yaml.v1.j23
-rw-r--r--roles/openshift_node/templates/openshift.docker.node.service4
-rw-r--r--roles/openshift_node/templates/openvswitch.docker.service2
-rw-r--r--roles/openshift_serviceaccounts/tasks/main.yml3
-rw-r--r--roles/os_zabbix/vars/template_openshift_master.yml35
-rw-r--r--test/env-setup2
-rwxr-xr-xtest/units/yedit_test.py233
-rw-r--r--utils/src/ooinstall/cli_installer.py59
-rw-r--r--utils/src/ooinstall/openshift_ansible.py16
-rw-r--r--utils/src/ooinstall/variants.py3
-rw-r--r--utils/test/cli_installer_tests.py5
139 files changed, 5624 insertions, 1195 deletions
diff --git a/.tito/packages/openshift-ansible b/.tito/packages/openshift-ansible
index fad006e05..7f350f435 100644
--- a/.tito/packages/openshift-ansible
+++ b/.tito/packages/openshift-ansible
@@ -1 +1 @@
-3.0.63-1 ./
+3.0.70-1 ./
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/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 deeea2e40..43b646c93 100644
--- a/inventory/byo/hosts.aep.example
+++ b/inventory/byo/hosts.aep.example
@@ -90,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 8963c2ad6..8b8dbade0 100644
--- a/inventory/byo/hosts.origin.example
+++ b/inventory/byo/hosts.origin.example
@@ -95,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 b931a684d..4c6aae0bd 100644
--- a/inventory/byo/hosts.ose.example
+++ b/inventory/byo/hosts.ose.example
@@ -91,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/openshift-ansible.spec b/openshift-ansible.spec
index 9f2d035a1..0ce628dfa 100644
--- a/openshift-ansible.spec
+++ b/openshift-ansible.spec
@@ -5,7 +5,7 @@
}
Name: openshift-ansible
-Version: 3.0.63
+Version: 3.0.70
Release: 1%{?dist}
Summary: Openshift and Atomic Enterprise Ansible
License: ASL 2.0
@@ -279,6 +279,69 @@ Atomic OpenShift Utilities includes
%changelog
+* 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)
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/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/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/pre.yml b/playbooks/common/openshift-cluster/upgrades/v3_1_to_v3_2/pre.yml
index 668a80996..6804de992 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
@@ -47,32 +47,39 @@
- name: Verify upgrade can proceed
hosts: oo_masters_to_config
- vars:
- openshift_master_ha: "{{ groups.oo_masters_to_config | length > 1 }}"
+ roles:
+ - openshift_facts
tasks:
- name: Ensure Master is running
service:
name: "{{ openshift.common.service_type }}-master"
state: started
enabled: yes
- when: not openshift_master_ha | bool and openshift.common.is_containerized | bool
+ 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 | bool and openshift.common.is_containerized | bool
+ 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 | bool and openshift.common.is_containerized | bool
+ 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:
@@ -85,6 +92,11 @@
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:
@@ -130,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
@@ -143,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','<')
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 d84d9f674..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,4 +1,33 @@
---
+# 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.
###############################################################################
@@ -16,14 +45,18 @@
###############################################################################
- 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:
@@ -63,6 +96,8 @@
hosts: oo_nodes_to_config
roles:
- openshift_facts
+ handlers:
+ - include: ../../../../../roles/openshift_node/handlers/main.yml
tasks:
- include: rpm_upgrade.yml
vars:
@@ -108,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-etcd/config.yml b/playbooks/common/openshift-etcd/config.yml
index 06fbd6862..2f07b2f51 100644
--- a/playbooks/common/openshift-etcd/config.yml
+++ b/playbooks/common/openshift-etcd/config.yml
@@ -103,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 972427c53..f1eaf8e16 100644
--- a/playbooks/common/openshift-master/config.yml
+++ b/playbooks/common/openshift-master/config.yml
@@ -311,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/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/etcd/tasks/main.yml b/roles/etcd/tasks/main.yml
index 064544b03..afec6b30b 100644
--- a/roles/etcd/tasks/main.yml
+++ b/roles/etcd/tasks/main.yml
@@ -39,7 +39,8 @@
- 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
diff --git a/roles/etcd/templates/etcd.docker.service b/roles/etcd/templates/etcd.docker.service
index defc070bd..b8dbefa64 100644
--- a/roles/etcd/templates/etcd.docker.service
+++ b/roles/etcd/templates/etcd.docker.service
@@ -13,4 +13,4 @@ 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..275789298
--- /dev/null
+++ b/roles/lib_openshift_api/build/ansible/edit.py
@@ -0,0 +1,78 @@
+# 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',
+ '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..0796d807e
--- /dev/null
+++ b/roles/lib_openshift_api/build/ansible/obj.py
@@ -0,0 +1,132 @@
+# 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',
+ '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..1e2a41077
--- /dev/null
+++ b/roles/lib_openshift_api/library/oc_edit.py
@@ -0,0 +1,605 @@
+#!/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+\])|(\w+)).?)+$"
+ re_key = r"(?:\[(-?\d+)\])|(\w+)"
+
+ 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])]
+
+ # expected dict entry
+ elif key_indexes[-1][1]:
+ if isinstance(data, dict):
+ del data[key_indexes[-1][1]]
+
+ @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):
+ ''' put key, value into a yaml file '''
+ try:
+ entry = Yedit.get_entry(self.yaml_dict, key)
+ except KeyError as _:
+ entry = None
+ if not entry:
+ return (False, self.yaml_dict)
+
+ Yedit.remove_entry(self.yaml_dict, key)
+ return (True, self.yaml_dict)
+
+ def put(self, key, value):
+ ''' put key, value into a yaml file '''
+ try:
+ entry = Yedit.get_entry(self.yaml_dict, key)
+ except KeyError as _:
+ entry = None
+
+ if entry == value:
+ return (False, self.yaml_dict)
+
+ Yedit.add_entry(self.yaml_dict, key, value)
+ return (True, self.yaml_dict)
+
+ def create(self, key, value):
+ ''' create the 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',
+ '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..27135e02e
--- /dev/null
+++ b/roles/lib_openshift_api/library/oc_obj.py
@@ -0,0 +1,688 @@
+#!/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+\])|(\w+)).?)+$"
+ re_key = r"(?:\[(-?\d+)\])|(\w+)"
+
+ 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])]
+
+ # expected dict entry
+ elif key_indexes[-1][1]:
+ if isinstance(data, dict):
+ del data[key_indexes[-1][1]]
+
+ @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):
+ ''' put key, value into a yaml file '''
+ try:
+ entry = Yedit.get_entry(self.yaml_dict, key)
+ except KeyError as _:
+ entry = None
+ if not entry:
+ return (False, self.yaml_dict)
+
+ Yedit.remove_entry(self.yaml_dict, key)
+ return (True, self.yaml_dict)
+
+ def put(self, key, value):
+ ''' put key, value into a yaml file '''
+ try:
+ entry = Yedit.get_entry(self.yaml_dict, key)
+ except KeyError as _:
+ entry = None
+
+ if entry == value:
+ return (False, self.yaml_dict)
+
+ Yedit.add_entry(self.yaml_dict, key, value)
+ return (True, self.yaml_dict)
+
+ def create(self, key, value):
+ ''' create the 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',
+ '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..8e5800e52
--- /dev/null
+++ b/roles/lib_openshift_api/library/oc_secret.py
@@ -0,0 +1,667 @@
+#!/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+\])|(\w+)).?)+$"
+ re_key = r"(?:\[(-?\d+)\])|(\w+)"
+
+ 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])]
+
+ # expected dict entry
+ elif key_indexes[-1][1]:
+ if isinstance(data, dict):
+ del data[key_indexes[-1][1]]
+
+ @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):
+ ''' put key, value into a yaml file '''
+ try:
+ entry = Yedit.get_entry(self.yaml_dict, key)
+ except KeyError as _:
+ entry = None
+ if not entry:
+ return (False, self.yaml_dict)
+
+ Yedit.remove_entry(self.yaml_dict, key)
+ return (True, self.yaml_dict)
+
+ def put(self, key, value):
+ ''' put key, value into a yaml file '''
+ try:
+ entry = Yedit.get_entry(self.yaml_dict, key)
+ except KeyError as _:
+ entry = None
+
+ if entry == value:
+ return (False, self.yaml_dict)
+
+ Yedit.add_entry(self.yaml_dict, key, value)
+ return (True, self.yaml_dict)
+
+ def create(self, key, value):
+ ''' create the 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_openshift_api/library/oc_secrets.py b/roles/lib_openshift_api/library/oc_secrets.py
deleted file mode 100644
index 841c14692..000000000
--- a/roles/lib_openshift_api/library/oc_secrets.py
+++ /dev/null
@@ -1,379 +0,0 @@
-#!/usr/bin/env python
-'''
-module for openshift cloud secrets
-'''
-# Examples:
-#
-# # to initiate and use /etc/origin/master/admin.kubeconfig file for auth
-# - name: list secrets
-# oc_secrets:
-# state: list
-# namespace: default
-#
-# # To get a specific secret named 'mysecret'
-# - name: list secrets
-# oc_secrets:
-# state: list
-# namespace: default
-# name: mysecret
-#
-# # To create a secret:
-# # This module expects the user to place the files on the remote server and pass them in.
-# - name: create a secret from file
-# oc_secrets:
-# state: present
-# namespace: default
-# name: mysecret
-# files:
-# - /tmp/config.yml
-# - /tmp/passwords.yml
-# delete_after: False
-
-# # To create a secret:
-# # This module expects the user to place the files on the remote server and pass them in.
-# - name: create a secret from content
-# oc_secrets:
-# state: present
-# namespace: default
-# name: mysecret
-# contents:
-# - path: /tmp/config.yml
-# content: "value=True\n"
-# - path: /tmp/passwords.yml
-# content: "test1\ntest2\ntest3\ntest4\n"
-#
-
-import os
-import shutil
-import json
-import atexit
-
-class OpenShiftOC(object):
- ''' 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 '''
- self.namespace = namespace
- self.name = secret_name
- self.verbose = verbose
- self.kubeconfig = kubeconfig
-
- def get_secrets(self):
- '''return a secret by name '''
- cmd = ['get', 'secrets', '-o', 'json', '-n', self.namespace]
- if self.name:
- cmd.append(self.name)
-
- 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 delete_secret(self):
- '''return all pods '''
- return self.oc_cmd(['delete', 'secrets', self.name, '-n', self.namespace])
-
- def secret_new(self, files):
- '''Create a secret with all pods '''
- 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)
-
- @staticmethod
- def create_files_from_contents(data):
- '''Turn an array of dict: filename, content into a files array'''
- files = []
- for sfile in data:
- with open(sfile['path'], 'w') as fds:
- fds.write(sfile['content'])
- files.append(sfile['path'])
-
- # Register cleanup when module is done
- atexit.register(OpenShiftOC.cleanup, files)
- return files
-
- def update_secret(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' % secret['results']['metadata']['name']
- with open(sfile_path, 'w') as sfd:
- sfd.write(json.dumps(secret['results']))
-
- cmd = ['replace', '-f', sfile_path]
- if force:
- cmd = ['replace', '--force', '-f', sfile_path]
-
- atexit.register(OpenShiftOC.cleanup, [sfile_path])
-
- return self.oc_cmd(cmd)
-
- def prep_secret(self, files):
- ''' return what the secret would look like if created
- This is accomplished by passing -ojson. This will most likely change in the future
- '''
- 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)
-
- def oc_cmd(self, cmd, output=False):
- '''Base command for oc '''
- cmds = ['/usr/bin/oc']
- cmds.extend(cmd)
-
- results = ''
-
- if self.verbose:
- print ' '.join(cmds)
-
- proc = subprocess.Popen(cmds,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- env={'KUBECONFIG': self.kubeconfig})
- proc.wait()
- if proc.returncode == 0:
- if output:
- try:
- results = json.loads(proc.stdout.read())
- except ValueError as err:
- if "No JSON object could be decoded" in err.message:
- results = err.message
-
- if self.verbose:
- print proc.stderr.read()
- print results
- print
-
- return {"returncode": proc.returncode, "results": results}
-
- return {"returncode": proc.returncode,
- "stderr": proc.stderr.read(),
- "stdout": proc.stdout.read(),
- "results": {}
- }
-
- @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)
-
-
-def exists(results, _name):
- ''' Check to see if the results include the name '''
- if not results:
- return False
-
- if find_result(results, _name):
- return True
-
- return False
-
-def find_result(results, _name):
- ''' Find the specified result by name'''
- rval = None
- for result in results:
- #print "%s == %s" % (result['metadata']['name'], name)
- if result.has_key('metadata') and result['metadata']['name'] == _name:
- rval = result
- break
-
- return rval
-
-# Disabling too-many-branches. This is a yaml dictionary comparison function
-# pylint: disable=too-many-branches,too-many-return-statements
-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 = ['creationTimestamp', 'selfLink', 'resourceVersion', 'uid', 'namespace']
-
- 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 = check_def_equal(user_def[key], value)
- 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
-
-
-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 = OpenShiftOC(module.params['namespace'],
- module.params['name'],
- kubeconfig=module.params['kubeconfig'],
- verbose=module.params['debug'])
-
- state = module.params['state']
-
- api_rval = occmd.get_secrets()
-
- #####
- # 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 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_secret()
- 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 = OpenShiftOC.create_files_from_contents(module.params['contents'])
- else:
- module.fail_json(msg='Either specify files or contents.')
-
- ########
- # Create
- ########
- if not 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.secret_new(files)
-
- # Remove files
- if files and module.params['delete_after']:
- OpenShiftOC.cleanup(files)
-
- module.exit_json(changed=True, results=api_rval, state="present")
-
- ########
- # Update
- ########
- secret = occmd.prep_secret(files)
-
- if secret['returncode'] != 0:
- module.fail_json(msg=secret)
-
- if check_def_equal(secret['results'], api_rval['results'][0]):
-
- # Remove files
- if files and module.params['delete_after']:
- OpenShiftOC.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_secret(files, force=module.params['force'])
-
- # Remove files
- if files and module.params['delete_after']:
- OpenShiftOC.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..a4c0d40b3
--- /dev/null
+++ b/roles/lib_yaml_editor/build/ansible/yedit.py
@@ -0,0 +1,66 @@
+#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)
+ module.exit_json(changed=rval[0], results=rval[1], state="present")
+
+ if not module.params['content']:
+ rval = yamlfile.create(module.params['key'], value)
+ else:
+ yamlfile.write()
+ rval = yamlfile.load()
+ 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..faef577ae
--- /dev/null
+++ b/roles/lib_yaml_editor/build/src/yedit.py
@@ -0,0 +1,201 @@
+# pylint: skip-file
+
+class YeditException(Exception):
+ ''' Exception class for Yedit '''
+ pass
+
+class Yedit(object):
+ ''' Class to modify yaml files '''
+ re_valid_key = r"(((\[-?\d+\])|(\w+)).?)+$"
+ re_key = r"(?:\[(-?\d+)\])|(\w+)"
+
+ 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])]
+
+ # expected dict entry
+ elif key_indexes[-1][1]:
+ if isinstance(data, dict):
+ del data[key_indexes[-1][1]]
+
+ @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):
+ ''' put key, value into a yaml file '''
+ try:
+ entry = Yedit.get_entry(self.yaml_dict, key)
+ except KeyError as _:
+ entry = None
+ if not entry:
+ return (False, self.yaml_dict)
+
+ Yedit.remove_entry(self.yaml_dict, key)
+ return (True, self.yaml_dict)
+
+ def put(self, key, value):
+ ''' put key, value into a yaml file '''
+ try:
+ entry = Yedit.get_entry(self.yaml_dict, key)
+ except KeyError as _:
+ entry = None
+
+ if entry == value:
+ return (False, self.yaml_dict)
+
+ Yedit.add_entry(self.yaml_dict, key, value)
+ return (True, self.yaml_dict)
+
+ def create(self, key, value):
+ ''' create the 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
index b75aff109..696ece63b 100644
--- a/roles/lib_yaml_editor/library/yedit.py
+++ b/roles/lib_yaml_editor/library/yedit.py
@@ -1,10 +1,27 @@
#!/usr/bin/env python
+# ___ ___ _ _ ___ ___ _ _____ ___ ___
+# / __| __| \| | __| _ \ /_\_ _| __| \
+# | (_ | _|| .` | _|| / / _ \| | | _|| |) |
+# \___|___|_|\_|___|_|_\/_/_\_\_|_|___|___/_ _____
+# | \ / _ \ | \| |/ _ \_ _| | __| \_ _|_ _|
+# | |) | (_) | | .` | (_) || | | _|| |) | | | |
+# |___/ \___/ |_|\_|\___/ |_| |___|___/___| |_|
+
'''
-module for openshift cloud secrets
+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 '''
@@ -12,11 +29,16 @@ class YeditException(Exception):
class Yedit(object):
''' Class to modify yaml files '''
+ re_valid_key = r"(((\[-?\d+\])|(\w+)).?)+$"
+ re_key = r"(?:\[(-?\d+)\])|(\w+)"
- def __init__(self, filename):
+ def __init__(self, filename=None, content=None, content_type='yaml'):
+ self.content = content
self.filename = filename
- self.__yaml_dict = None
- self.get()
+ 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):
@@ -29,63 +51,97 @@ class Yedit(object):
self.__yaml_dict = value
@staticmethod
- def remove_entry(data, keys):
- ''' remove an item from a dictionary with key notation a.b.c
- d = {'a': {'b': 'c'}}}
- keys = a.b
- item = c
- '''
- if "." in keys:
- key, rest = keys.split(".", 1)
- if key in data.keys():
- Yedit.remove_entry(data[key], rest)
- else:
- del data[keys]
+ 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])]
+
+ # expected dict entry
+ elif key_indexes[-1][1]:
+ if isinstance(data, dict):
+ del data[key_indexes[-1][1]]
@staticmethod
- def add_entry(data, keys, item):
- ''' Add an item to a dictionary with key notation a.b.c
+ def add_entry(data, key, item=None):
+ ''' Get an item from a dictionary with key notation a.b.c
d = {'a': {'b': 'c'}}}
- keys = a.b
- item = c
+ key = a.b
+ return c
'''
- if "." in keys:
- key, rest = keys.split(".", 1)
- if key not in data:
- data[key] = {}
+ 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]
- if not isinstance(data, dict):
- raise YeditException('Invalid add_entry called on a [%s] of type [%s].' % (data, type(data)))
+ elif arr_ind and isinstance(data, list) and int(arr_ind) <= len(data) - 1:
+ data = data[int(arr_ind)]
else:
- Yedit.add_entry(data[key], rest, item)
+ return None
- else:
- data[keys] = item
+ # 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, keys):
+ def get_entry(data, key):
''' Get an item from a dictionary with key notation a.b.c
d = {'a': {'b': 'c'}}}
- keys = a.b
+ key = a.b
return c
'''
- if keys and "." in keys:
- key, rest = keys.split(".", 1)
- if not isinstance(data[key], dict):
- raise YeditException('Invalid get_entry called on a [%s] of type [%s].' % (data, type(data)))
+ 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 Yedit.get_entry(data[key], rest)
-
- else:
- return data.get(keys, None)
+ 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.dump(self.yaml_dict, default_flow_style=False))
+ yfd.write(yaml.safe_dump(self.yaml_dict, default_flow_style=False))
def read(self):
''' write to file '''
@@ -105,7 +161,8 @@ class Yedit(object):
return True
return False
- def get(self):
+
+ def load(self, content_type='yaml'):
''' return yaml file '''
contents = self.read()
@@ -114,13 +171,25 @@ class Yedit(object):
# check if it is yaml
try:
- self.yaml_dict = yaml.load(contents)
+ 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
+ # 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):
''' put key, value into a yaml file '''
try:
@@ -131,8 +200,7 @@ class Yedit(object):
return (False, self.yaml_dict)
Yedit.remove_entry(self.yaml_dict, key)
- self.write()
- return (True, self.get())
+ return (True, self.yaml_dict)
def put(self, key, value):
''' put key, value into a yaml file '''
@@ -145,18 +213,15 @@ class Yedit(object):
return (False, self.yaml_dict)
Yedit.add_entry(self.yaml_dict, key, value)
- self.write()
- return (True, self.get())
+ return (True, self.yaml_dict)
def create(self, key, value):
''' create the file '''
if not self.exists():
self.yaml_dict = {key: value}
- self.write()
- return (True, self.get())
-
- return (False, self.get())
+ return (True, self.yaml_dict)
+ return (False, self.yaml_dict)
def main():
'''
@@ -168,24 +233,24 @@ def main():
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=[["contents", "files"]],
+ #mutually_exclusive=[["src", "content"]],
supports_check_mode=True,
)
state = module.params['state']
- yamlfile = Yedit(module.params['src'])
+ yamlfile = Yedit(module.params['src'], module.params['content'])
- rval = yamlfile.get()
+ rval = yamlfile.load()
if not rval and state != 'present':
module.fail_json(msg='Error opening file [%s]. Verify that the' + \
- ' file exists and that it is has correct permissions.')
+ ' file exists, that it is has correct permissions, and is valid yaml.')
if state == 'list':
module.exit_json(changed=False, results=rval, state="list")
@@ -205,7 +270,11 @@ def main():
rval = yamlfile.put(module.params['key'], value)
module.exit_json(changed=rval[0], results=rval[1], state="present")
- rval = yamlfile.create(module.params['key'], value)
+ if not module.params['content']:
+ rval = yamlfile.create(module.params['key'], value)
+ else:
+ yamlfile.write()
+ rval = yamlfile.load()
module.exit_json(changed=rval[0], results=rval[1], state="present")
module.exit_json(failed=True,
diff --git a/roles/openshift_cli/meta/main.yml b/roles/openshift_cli/meta/main.yml
index 62de120c6..2c982e278 100644
--- a/roles/openshift_cli/meta/main.yml
+++ b/roles/openshift_cli/meta/main.yml
@@ -12,6 +12,7 @@ galaxy_info:
categories:
- cloud
dependencies:
-- role: openshift_common
- role: openshift_docker
when: openshift.common.is_containerized | bool
+- role: openshift_common
+- role: openshift_cli_facts
diff --git a/roles/openshift_cli/tasks/main.yml b/roles/openshift_cli/tasks/main.yml
index e82903b81..bfa60e5b0 100644
--- a/roles/openshift_cli/tasks/main.yml
+++ b/roles/openshift_cli/tasks/main.yml
@@ -1,10 +1,4 @@
---
-# TODO: move this to a new 'cli' role
-- openshift_facts:
- role: common
- local_facts:
- 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
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/defaults/main.yml b/roles/openshift_cloud_provider/defaults/main.yml
deleted file mode 100644
index 6c7403232..000000000
--- a/roles/openshift_cloud_provider/defaults/main.yml
+++ /dev/null
@@ -1,7 +0,0 @@
----
-ocp_os_auth_url: "{{ lookup('env', 'OS_AUTH_URL') }}"
-ocp_os_username: "{{ lookup('env', 'OS_USERNAME') }}"
-ocp_os_password: "{{ lookup('env', 'OS_PASSWORD') }}"
-ocp_os_tenant_id: "{{ lookup('env', 'OS_TENANT_ID') }}"
-ocp_os_tenant_name: "{{ lookup('env', 'OS_TENANT_NAME') }}"
-ocp_os_region: "{{ lookup('env', 'OS_REGION_NAME') }}"
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
index e14f944e8..471fd686b 100644
--- a/roles/openshift_cloud_provider/tasks/main.yml
+++ b/roles/openshift_cloud_provider/tasks/main.yml
@@ -1,3 +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: "openshift_cloud_provider is defined and openshift_cloud_provider == 'openstack' and 'provider' in openshift and openshift.provider.name == 'openstack'"
+ 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
index a56f1891a..c501121e5 100644
--- a/roles/openshift_cloud_provider/tasks/openstack.yml
+++ b/roles/openshift_cloud_provider/tasks/openstack.yml
@@ -1,9 +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 /etc/cloud.conf
+- name: Create cloud config
template:
- dest: /etc/cloud.conf
- src: openstack/cloud.conf.j2
- when: ocp_os_auth_url and ocp_os_username and ocp_os_password and (ocp_os_tenant_id or ocp_os_tenant_name) \ No newline at end of file
+ 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/templates/openstack/cloud.conf.j2 b/roles/openshift_cloud_provider/templates/openstack/cloud.conf.j2
deleted file mode 100644
index 388f3a735..000000000
--- a/roles/openshift_cloud_provider/templates/openstack/cloud.conf.j2
+++ /dev/null
@@ -1,17 +0,0 @@
-[Global]
-auth-url = {{ ocp_os_auth_url }}
-username = {{ ocp_os_username }}
-password = {{ ocp_os_password }}
-{% if ocp_os_tenant_id %}
-tenant-id = {{ ocp_os_tenant_id }}
-{% else %}
-tenant-name = {{ ocp_os_tenant_name }}
-{% endif %}
-{% if ocp_os_region %}
-region = {{ ocp_os_region }}
-{% endif %}
-{% if ocp_os_lb_subnet_id is defined %}
-+
-+[LoadBalancer]
-+subnet-id = {{ ocp_os_lb_subnet_id }}
-+{% endif %} \ No newline at end of file
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 d879db0aa..02150406d 100644
--- a/roles/openshift_common/meta/main.yml
+++ b/roles/openshift_common/meta/main.yml
@@ -12,7 +12,6 @@ galaxy_info:
categories:
- cloud
dependencies:
-- { role: os_firewall }
-- { role: openshift_facts }
-- { role: openshift_repos }
-- { role: openshift_cloud_provider }
+- 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 541efe27a..b6074ff64 100644
--- a/roles/openshift_common/tasks/main.yml
+++ b/roles/openshift_common/tasks/main.yml
@@ -28,8 +28,12 @@
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_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 fe9effc19..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
@@ -61,7 +61,7 @@
"from": {
"kind": "ImageStreamTag",
"name": "mongodb:latest",
- "namespace": "openshift"
+ "namespace": "${NAMESPACE}"
},
"lastTriggeredImage": ""
}
@@ -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.1/db-templates/mongodb-persistent-template.json b/roles/openshift_examples/files/examples/v1.1/db-templates/mongodb-persistent-template.json
index ab37e7a3a..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
@@ -78,7 +78,7 @@
"from": {
"kind": "ImageStreamTag",
"name": "mongodb:latest",
- "namespace": "openshift"
+ "namespace": "${NAMESPACE}"
},
"lastTriggeredImage": ""
}
@@ -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.1/db-templates/mysql-ephemeral-template.json b/roles/openshift_examples/files/examples/v1.1/db-templates/mysql-ephemeral-template.json
index 2c28db746..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
@@ -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.1/db-templates/mysql-persistent-template.json b/roles/openshift_examples/files/examples/v1.1/db-templates/mysql-persistent-template.json
index 94199b6fe..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
@@ -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.1/db-templates/postgresql-ephemeral-template.json b/roles/openshift_examples/files/examples/v1.1/db-templates/postgresql-ephemeral-template.json
index da548c591..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
@@ -61,7 +61,7 @@
"from": {
"kind": "ImageStreamTag",
"name": "postgresql:latest",
- "namespace": "openshift"
+ "namespace": "${NAMESPACE}"
},
"lastTriggeredImage": ""
}
@@ -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.1/db-templates/postgresql-persistent-template.json b/roles/openshift_examples/files/examples/v1.1/db-templates/postgresql-persistent-template.json
index df8a34a9e..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
@@ -78,7 +78,7 @@
"from": {
"kind": "ImageStreamTag",
"name": "postgresql:latest",
- "namespace": "openshift"
+ "namespace": "${NAMESPACE}"
},
"lastTriggeredImage": ""
}
@@ -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.1/quickstart-templates/cakephp-mysql.json b/roles/openshift_examples/files/examples/v1.1/quickstart-templates/cakephp-mysql.json
index 6e6f4f096..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
@@ -82,7 +82,7 @@
"sourceStrategy": {
"from": {
"kind": "ImageStreamTag",
- "namespace": "openshift",
+ "namespace": "${NAMESPACE}",
"name": "php:5.6"
}
}
@@ -282,7 +282,7 @@
],
"from": {
"kind": "ImageStreamTag",
- "namespace": "openshift",
+ "namespace": "${NAMESPACE}",
"name": "mysql:5.6"
}
}
@@ -361,6 +361,12 @@
"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.",
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 21e29ae30..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
@@ -82,7 +82,7 @@
"sourceStrategy": {
"from": {
"kind": "ImageStreamTag",
- "namespace": "openshift",
+ "namespace": "${NAMESPACE}",
"name": "php:5.6"
}
}
@@ -236,6 +236,12 @@
"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.",
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 20d9ac2b2..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
@@ -82,7 +82,7 @@
"sourceStrategy": {
"from": {
"kind": "ImageStreamTag",
- "namespace": "openshift",
+ "namespace": "${NAMESPACE}",
"name": "perl:5.20"
}
}
@@ -256,7 +256,7 @@
],
"from": {
"kind": "ImageStreamTag",
- "namespace": "openshift",
+ "namespace": "${NAMESPACE}",
"name": "mysql:5.6"
}
}
@@ -335,6 +335,12 @@
"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.",
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 17a114cbb..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
@@ -82,7 +82,7 @@
"sourceStrategy": {
"from": {
"kind": "ImageStreamTag",
- "namespace": "openshift",
+ "namespace": "${NAMESPACE}",
"name": "perl:5.20"
}
}
@@ -204,6 +204,12 @@
"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.",
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 063591a8f..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
@@ -82,7 +82,7 @@
"sourceStrategy": {
"from": {
"kind": "ImageStreamTag",
- "namespace": "openshift",
+ "namespace": "${NAMESPACE}",
"name": "python:3.4"
}
}
@@ -266,7 +266,7 @@
],
"from": {
"kind": "ImageStreamTag",
- "namespace": "openshift",
+ "namespace": "${NAMESPACE}",
"name": "postgresql:9.4"
}
}
@@ -345,6 +345,12 @@
"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.",
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 a36678ba6..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
@@ -82,7 +82,7 @@
"sourceStrategy": {
"from": {
"kind": "ImageStreamTag",
- "namespace": "openshift",
+ "namespace": "${NAMESPACE}",
"name": "python:3.4"
}
}
@@ -231,6 +231,12 @@
"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.",
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 bbe6713ff..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
@@ -78,7 +78,7 @@
"from": {
"kind": "ImageStreamTag",
"name": "jenkins:latest",
- "namespace": "openshift"
+ "namespace": "${NAMESPACE}"
},
"lastTriggeredImage": ""
}
@@ -163,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.1/quickstart-templates/jenkins-persistent-template.json b/roles/openshift_examples/files/examples/v1.1/quickstart-templates/jenkins-persistent-template.json
index d98e729d4..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
@@ -95,7 +95,7 @@
"from": {
"kind": "ImageStreamTag",
"name": "jenkins:latest",
- "namespace": "openshift"
+ "namespace": "${NAMESPACE}"
},
"lastTriggeredImage": ""
}
@@ -180,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.1/quickstart-templates/nodejs-mongodb.json b/roles/openshift_examples/files/examples/v1.1/quickstart-templates/nodejs-mongodb.json
index e352b15f3..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
@@ -82,7 +82,7 @@
"sourceStrategy": {
"from": {
"kind": "ImageStreamTag",
- "namespace": "openshift",
+ "namespace": "${NAMESPACE}",
"name": "nodejs:0.10"
}
}
@@ -261,7 +261,7 @@
],
"from": {
"kind": "ImageStreamTag",
- "namespace": "openshift",
+ "namespace": "${NAMESPACE}",
"name": "mongodb:2.6"
}
}
@@ -344,6 +344,12 @@
"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.",
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 da16ec157..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
@@ -82,7 +82,7 @@
"sourceStrategy": {
"from": {
"kind": "ImageStreamTag",
- "namespace": "openshift",
+ "namespace": "${NAMESPACE}",
"name": "nodejs:0.10"
}
}
@@ -231,6 +231,12 @@
"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.",
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 99b6513f2..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
@@ -82,7 +82,7 @@
"sourceStrategy": {
"from": {
"kind": "ImageStreamTag",
- "namespace": "openshift",
+ "namespace": "${NAMESPACE}",
"name": "ruby:2.2"
}
}
@@ -293,7 +293,7 @@
],
"from": {
"kind": "ImageStreamTag",
- "namespace": "openshift",
+ "namespace": "${NAMESPACE}",
"name": "postgresql:9.4"
}
}
@@ -380,6 +380,12 @@
"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.",
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 fe9effc19..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": ""
}
@@ -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 ab37e7a3a..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": ""
}
@@ -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 2c28db746..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 94199b6fe..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 da548c591..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": ""
}
@@ -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 df8a34a9e..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": ""
}
@@ -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/quickstart-templates/cakephp-mysql.json b/roles/openshift_examples/files/examples/v1.2/quickstart-templates/cakephp-mysql.json
index 6e6f4f096..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
@@ -82,7 +82,7 @@
"sourceStrategy": {
"from": {
"kind": "ImageStreamTag",
- "namespace": "openshift",
+ "namespace": "${NAMESPACE}",
"name": "php:5.6"
}
}
@@ -282,7 +282,7 @@
],
"from": {
"kind": "ImageStreamTag",
- "namespace": "openshift",
+ "namespace": "${NAMESPACE}",
"name": "mysql:5.6"
}
}
@@ -361,6 +361,12 @@
"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.",
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 21e29ae30..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
@@ -82,7 +82,7 @@
"sourceStrategy": {
"from": {
"kind": "ImageStreamTag",
- "namespace": "openshift",
+ "namespace": "${NAMESPACE}",
"name": "php:5.6"
}
}
@@ -236,6 +236,12 @@
"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.",
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 20d9ac2b2..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
@@ -82,7 +82,7 @@
"sourceStrategy": {
"from": {
"kind": "ImageStreamTag",
- "namespace": "openshift",
+ "namespace": "${NAMESPACE}",
"name": "perl:5.20"
}
}
@@ -256,7 +256,7 @@
],
"from": {
"kind": "ImageStreamTag",
- "namespace": "openshift",
+ "namespace": "${NAMESPACE}",
"name": "mysql:5.6"
}
}
@@ -335,6 +335,12 @@
"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.",
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 17a114cbb..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
@@ -82,7 +82,7 @@
"sourceStrategy": {
"from": {
"kind": "ImageStreamTag",
- "namespace": "openshift",
+ "namespace": "${NAMESPACE}",
"name": "perl:5.20"
}
}
@@ -204,6 +204,12 @@
"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.",
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 063591a8f..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
@@ -82,7 +82,7 @@
"sourceStrategy": {
"from": {
"kind": "ImageStreamTag",
- "namespace": "openshift",
+ "namespace": "${NAMESPACE}",
"name": "python:3.4"
}
}
@@ -266,7 +266,7 @@
],
"from": {
"kind": "ImageStreamTag",
- "namespace": "openshift",
+ "namespace": "${NAMESPACE}",
"name": "postgresql:9.4"
}
}
@@ -345,6 +345,12 @@
"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.",
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 a36678ba6..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
@@ -82,7 +82,7 @@
"sourceStrategy": {
"from": {
"kind": "ImageStreamTag",
- "namespace": "openshift",
+ "namespace": "${NAMESPACE}",
"name": "python:3.4"
}
}
@@ -231,6 +231,12 @@
"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.",
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 bbe6713ff..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": ""
}
@@ -163,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 d98e729d4..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": ""
}
@@ -180,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 e352b15f3..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
@@ -82,7 +82,7 @@
"sourceStrategy": {
"from": {
"kind": "ImageStreamTag",
- "namespace": "openshift",
+ "namespace": "${NAMESPACE}",
"name": "nodejs:0.10"
}
}
@@ -261,7 +261,7 @@
],
"from": {
"kind": "ImageStreamTag",
- "namespace": "openshift",
+ "namespace": "${NAMESPACE}",
"name": "mongodb:2.6"
}
}
@@ -344,6 +344,12 @@
"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.",
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 da16ec157..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
@@ -82,7 +82,7 @@
"sourceStrategy": {
"from": {
"kind": "ImageStreamTag",
- "namespace": "openshift",
+ "namespace": "${NAMESPACE}",
"name": "nodejs:0.10"
}
}
@@ -231,6 +231,12 @@
"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.",
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 99b6513f2..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
@@ -82,7 +82,7 @@
"sourceStrategy": {
"from": {
"kind": "ImageStreamTag",
- "namespace": "openshift",
+ "namespace": "${NAMESPACE}",
"name": "ruby:2.2"
}
}
@@ -293,7 +293,7 @@
],
"from": {
"kind": "ImageStreamTag",
- "namespace": "openshift",
+ "namespace": "${NAMESPACE}",
"name": "postgresql:9.4"
}
}
@@ -380,6 +380,12 @@
"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.",
diff --git a/roles/openshift_facts/library/openshift_facts.py b/roles/openshift_facts/library/openshift_facts.py
index 263daf210..0d31d4ddf 100755
--- a/roles/openshift_facts/library/openshift_facts.py
+++ b/roles/openshift_facts/library/openshift_facts.py
@@ -26,6 +26,8 @@ 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):
@@ -332,14 +334,10 @@ def normalize_provider_facts(provider, metadata):
facts = dict(name=provider, metadata=metadata,
network=dict(interfaces=[], ipv6_enabled=False))
- if os.path.exists('/etc/cloud.conf'):
- for arg in ('api_server_args', 'controller_args', 'kubelet_args'):
- facts[arg] = {'cloud-provider': [provider],
- 'cloud-config': ['/etc/cloud.conf']}
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)
@@ -749,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')
@@ -918,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
@@ -959,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
@@ -1084,31 +1179,6 @@ def merge_facts(orig, new, additive_facts_to_overwrite, protected_facts_to_overw
facts[key] = copy.deepcopy(new[key])
return facts
-
-def merge_provider_facts(facts):
- """ Recursively merge provider facts dicts
-
- Args:
- facts (dict): existing facts
- Returns:
- dict: the facts dict updated with the provider config
- """
- if 'provider' not in facts:
- return facts
- if 'master' in facts:
- for arg in ('api_server_args', 'controller_args'):
- facts['master'][arg] = merge_facts(
- facts['provider'].get(arg, {}),
- facts['master'].get(arg, {}),
- [], [])
- if 'node' in facts:
- facts['node']['kubelet_args'] = merge_facts(
- facts['provider'].get('kubelet_args', {}),
- facts['node'].get('kubelet_args', {}),
- [], [])
- return facts
-
-
def save_local_facts(filename, facts):
""" Save local facts
@@ -1167,6 +1237,7 @@ def safe_get_bool(fact):
"""
return bool(strtobool(str(fact)))
+# pylint: disable=too-many-statements
def set_container_facts_if_unset(facts):
""" Set containerized facts.
@@ -1183,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']:
@@ -1214,8 +1305,10 @@ def set_container_facts_if_unset(facts):
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
@@ -1281,13 +1374,20 @@ class OpenShiftFacts(object):
Raises:
OpenShiftFactsUnsupportedRoleError:
"""
- known_roles = ['common', 'master', 'node', 'etcd', 'hosted', 'docker']
+ 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
@@ -1300,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
@@ -1322,6 +1424,7 @@ 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()
@@ -1338,7 +1441,6 @@ class OpenShiftFacts(object):
local_facts,
additive_facts_to_overwrite,
protected_facts_to_overwrite)
- facts = merge_provider_facts(facts)
facts['current_config'] = get_current_config(facts)
facts = set_url_facts_if_unset(facts)
facts = set_project_cfg_facts_if_unset(facts)
@@ -1350,11 +1452,14 @@ 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 safe_get_bool(facts['common']['is_containerized']):
facts = set_installed_variant_rpm_facts(facts)
return dict(openshift=facts)
@@ -1387,6 +1492,19 @@ class OpenShiftFacts(object):
debug_level=2)
if 'master' in roles:
+ 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,
@@ -1402,15 +1520,26 @@ class OpenShiftFacts(object):
session_secrets_file='',
access_token_max_seconds=86400,
auth_token_max_seconds=500,
- oauth_grant_method='auto')
+ oauth_grant_method='auto',
+ scheduler_predicates=scheduler_predicates,
+ scheduler_priorities=scheduler_priorities)
if 'node' in roles:
defaults['node'] = dict(labels={}, annotations={},
iptables_sync_period='5s',
+ local_quota_per_fsgroup="",
set_node_ip=False)
if 'docker' in roles:
- defaults['docker'] = dict(disable_push_dockerhub=False)
+ 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(
@@ -1430,7 +1559,6 @@ class OpenShiftFacts(object):
)
)
-
return defaults
def guess_host_provider(self):
@@ -1466,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):
@@ -1508,11 +1636,53 @@ 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 local facts
@@ -1540,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]:
@@ -1669,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,
@@ -1679,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'
@@ -1688,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 50e7e5747..a2eb27fbb 100644
--- a/roles/openshift_facts/tasks/main.yml
+++ b/roles/openshift_facts/tasks/main.yml
@@ -30,6 +30,6 @@
cluster_id: "{{ openshift_cluster_id | default('default') }}"
hostname: "{{ openshift_hostname | default(None) }}"
ip: "{{ openshift_ip | default(None) }}"
- is_containerized: "{{ containerized | 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 e5b9e4977..70c7ef4e4 100644
--- a/roles/openshift_master/handlers/main.yml
+++ b/roles/openshift_master/handlers/main.yml
@@ -1,17 +1,17 @@
---
- 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
diff --git a/roles/openshift_master/meta/main.yml b/roles/openshift_master/meta/main.yml
index 4eda4a8e2..8ae6bc371 100644
--- a/roles/openshift_master/meta/main.yml
+++ b/roles/openshift_master/meta/main.yml
@@ -13,3 +13,5 @@ galaxy_info:
- cloud
dependencies:
- 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 9c3d09d09..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:
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 c848e0ac2..7f1576682 100644
--- a/roles/openshift_master/templates/atomic-openshift-master.j2
+++ b/roles/openshift_master/templates/atomic-openshift-master.j2
@@ -4,6 +4,11 @@ CONFIG_FILE={{ openshift_master_config_file }}
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 6a21a04ab..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
@@ -22,5 +22,5 @@ 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 69f68d843..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
@@ -21,4 +21,4 @@ SyslogIdentifier={{ openshift.common.service_type }}-master-controllers
Restart=on-failure
[Install]
-WantedBy=multi-user.target
+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 b714fdeb7..6bd0dcf56 100644
--- a/roles/openshift_master/templates/docker/master.docker.service.j2
+++ b/roles/openshift_master/templates/docker/master.docker.service.j2
@@ -14,4 +14,4 @@ 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 8e2d927aa..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
@@ -4,6 +4,11 @@ CONFIG_FILE={{ openshift_master_config_file }}
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.j2 b/roles/openshift_master/templates/native-cluster/atomic-openshift-master-controllers.j2
index 5c6cb2dcb..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
@@ -4,6 +4,11 @@ CONFIG_FILE={{ openshift_master_config_file }}
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/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/meta/main.yml b/roles/openshift_node/meta/main.yml
index 702012489..84ba9ac2e 100644
--- a/roles/openshift_node/meta/main.yml
+++ b/roles/openshift_node/meta/main.yml
@@ -12,5 +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 4b5832ab7..80b3e710d 100644
--- a/roles/openshift_node/tasks/main.yml
+++ b/roles/openshift_node/tasks/main.yml
@@ -31,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.
@@ -52,27 +53,8 @@
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
@@ -99,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
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 53b1d6230..a8accca47 100644
--- a/roles/openshift_node/templates/openshift.docker.node.service
+++ b/roles/openshift_node/templates/openshift.docker.node.service
@@ -12,11 +12,11 @@ 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 --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 {{ 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 047f66fda..8052a3a39 100644
--- a/roles/openshift_node/templates/openvswitch.docker.service
+++ b/roles/openshift_node/templates/openvswitch.docker.service
@@ -13,4 +13,4 @@ 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/os_zabbix/vars/template_openshift_master.yml b/roles/os_zabbix/vars/template_openshift_master.yml
index 705066b35..a38db9f65 100644
--- a/roles/os_zabbix/vars/template_openshift_master.yml
+++ b/roles/os_zabbix/vars/template_openshift_master.yml
@@ -93,6 +93,18 @@ g_template_openshift_master:
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
value_type: int
@@ -279,6 +291,29 @@ g_template_openshift_master:
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'
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
index cdd2d2b59..09a65e888 100755
--- a/test/units/yedit_test.py
+++ b/test/units/yedit_test.py
@@ -5,170 +5,18 @@
import unittest
import os
-import yaml
-
-class YeditException(Exception):
- ''' Exception class for Yedit '''
- pass
-
-class Yedit(object):
- ''' Class to modify yaml files '''
-
- def __init__(self, filename):
- self.filename = filename
- self.__yaml_dict = None
- self.get()
-
- @property
- def yaml_dict(self):
- ''' get property 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, keys):
- ''' remove an item from a dictionary with key notation a.b.c
- d = {'a': {'b': 'c'}}}
- keys = a.b
- item = c
- '''
- if "." in keys:
- key, rest = keys.split(".", 1)
- if key in data.keys():
- Yedit.remove_entry(data[key], rest)
- else:
- del data[keys]
-
- @staticmethod
- def add_entry(data, keys, item):
- ''' Add an item to a dictionary with key notation a.b.c
- d = {'a': {'b': 'c'}}}
- keys = a.b
- item = c
- '''
- if "." in keys:
- key, rest = keys.split(".", 1)
- if key not in data:
- data[key] = {}
-
- if not isinstance(data, dict):
- raise YeditException('Invalid add_entry called on data [%s].' % data)
- else:
- Yedit.add_entry(data[key], rest, item)
-
- else:
- data[keys] = item
-
-
- @staticmethod
- def get_entry(data, keys):
- ''' Get an item from a dictionary with key notation a.b.c
- d = {'a': {'b': 'c'}}}
- keys = a.b
- return c
- '''
- if keys and "." in keys:
- key, rest = keys.split(".", 1)
- if not isinstance(data[key], dict):
- raise YeditException('Invalid get_entry called on a [%s] of type [%s].' % (data, type(data)))
-
- else:
- return Yedit.get_entry(data[key], rest)
-
- else:
- return data.get(keys, None)
-
-
- def write(self):
- ''' write to file '''
- with open(self.filename, 'w') as yfd:
- yfd.write(yaml.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 get(self):
- ''' return yaml file '''
- contents = self.read()
-
- if not contents:
- return None
-
- # check if it is yaml
- try:
- self.yaml_dict = yaml.load(contents)
- except yaml.YAMLError as _:
- # Error loading yaml
- return None
-
- return self.yaml_dict
-
- def delete(self, key):
- ''' put key, value into a yaml file '''
- try:
- entry = Yedit.get_entry(self.yaml_dict, key)
- except KeyError as _:
- entry = None
- if not entry:
- return (False, self.yaml_dict)
-
- Yedit.remove_entry(self.yaml_dict, key)
- self.write()
- return (True, self.get())
-
- def put(self, key, value):
- ''' put key, value into a yaml file '''
- try:
- entry = Yedit.get_entry(self.yaml_dict, key)
- except KeyError as _:
- entry = None
-
- if entry == value:
- return (False, self.yaml_dict)
-
- Yedit.add_entry(self.yaml_dict, key, value)
- self.write()
- return (True, self.get())
-
- def create(self, key, value):
- ''' create the file '''
- if not self.exists():
- self.yaml_dict = {key: value}
- self.write()
- return (True, self.get())
-
- return (False, self.get())
-
-
# Removing invalid variable names for tests so that I can
# keep them brief
-# pylint: disable=invalid-name
+# 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', 'f', 'g']}},
+ 'b': {'c': {'d': [{'e': 'x'}, 'f', 'g']}},
}
filename = 'yedit_test.yml'
@@ -179,10 +27,9 @@ class YeditTest(unittest.TestCase):
yed.yaml_dict = YeditTest.data
yed.write()
- def test_get(self):
+ def test_load(self):
''' Testing a get '''
yed = Yedit('yedit_test.yml')
-
self.assertEqual(yed.yaml_dict, self.data)
def test_write(self):
@@ -190,7 +37,6 @@ class YeditTest(unittest.TestCase):
yed = Yedit('yedit_test.yml')
yed.put('key1', 1)
yed.write()
- yed.get()
self.assertTrue(yed.yaml_dict.has_key('key1'))
self.assertEqual(yed.yaml_dict['key1'], 1)
@@ -199,14 +45,15 @@ class YeditTest(unittest.TestCase):
yed = Yedit('yedit_test.yml')
yed.put('x.y.z', 'modified')
yed.write()
- self.assertEqual(Yedit.get_entry(yed.get(), 'x.y.z'), 'modified')
+ 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.get()
+ yed.load()
self.assertTrue(not yed.yaml_dict.has_key('a'))
def test_delete_b_c(self):
@@ -214,7 +61,7 @@ class YeditTest(unittest.TestCase):
yed = Yedit('yedit_test.yml')
yed.delete('b.c')
yed.write()
- yed.get()
+ yed.load()
self.assertTrue(yed.yaml_dict.has_key('b'))
self.assertFalse(yed.yaml_dict['b'].has_key('c'))
@@ -224,10 +71,70 @@ class YeditTest(unittest.TestCase):
yed = Yedit('yedit_test.yml')
yed.create('foo', 'bar')
yed.write()
- yed.get()
+ 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)
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 2b95702bf..6e43eac9f 100644
--- a/utils/src/ooinstall/openshift_ansible.py
+++ b/utils/src/ooinstall/openshift_ansible.py
@@ -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)