summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTobias Urdin <tobias.urdin@binero.se>2019-02-08 22:31:43 +0100
committerTobias Urdin <tobias.urdin@binero.se>2019-02-09 10:32:34 +0100
commita9322c04b6a22baecd5b358c050e4dc14a21444d (patch)
treedfa7837faba79e49b2482b3fe4319a05ae238761
parentdd8c88354e655df35a55d1a3015c7d0f98949531 (diff)
Rework upload-forge role to use module
Renames the role to upload-forge because you can actually run your own Forge server if you want. This patch adds a custom module to the upload-forge role that provides the "forge_upload" module. This directly interacts with the a Forge API to upload the module. The only dependency is that the python requests module is installed. Change-Id: I5749364bd2c29ad6df866c2bd5a3584c8419f709
Notes
Notes (review): Code-Review+2: Jeremy Stanley <fungi@yuggoth.org> Code-Review+2: Andreas Jaeger <jaegerandi@gmail.com> Workflow+1: Andreas Jaeger <jaegerandi@gmail.com> Verified+2: Zuul Submitted-by: Zuul Submitted-at: Wed, 13 Feb 2019 17:39:30 +0000 Reviewed-on: https://review.openstack.org/635941 Project: openstack-infra/zuul-jobs Branch: refs/heads/master
-rw-r--r--roles/upload-forge/README.rst24
-rw-r--r--roles/upload-forge/__init__.py0
-rw-r--r--roles/upload-forge/defaults/main.yaml2
-rw-r--r--roles/upload-forge/library/__init__.py0
-rw-r--r--roles/upload-forge/library/forge_upload.py182
-rw-r--r--roles/upload-forge/library/test_forge_upload.py28
-rw-r--r--roles/upload-forge/tasks/main.yaml13
-rw-r--r--roles/upload-puppetforge/README.rst22
-rw-r--r--roles/upload-puppetforge/defaults/main.yaml3
-rw-r--r--roles/upload-puppetforge/tasks/main.yaml52
10 files changed, 249 insertions, 77 deletions
diff --git a/roles/upload-forge/README.rst b/roles/upload-forge/README.rst
new file mode 100644
index 0000000..a71d2ff
--- /dev/null
+++ b/roles/upload-forge/README.rst
@@ -0,0 +1,24 @@
1Upload puppet module tarball to a Forge server
2
3This role requires the python requests module to be
4installed where Ansible is executing this role.
5
6**Role Variables**
7
8 .. zuul:rolevar:: forge_url
9 :default: https://forgeapi.puppet.com
10
11 The URL to the Puppet Forge API.
12
13 .. zuul:rolevar:: forge_username
14
15 Username to use to log in to Puppet Forge.
16
17 .. zuul:rolevar:: forge_password
18
19 Password to use to log in to Puppet Forge.
20
21 .. zuul:rolevar:: forge_tarball
22
23 Absolute path to the module tarball that should be
24 uploaded.
diff --git a/roles/upload-forge/__init__.py b/roles/upload-forge/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/roles/upload-forge/__init__.py
diff --git a/roles/upload-forge/defaults/main.yaml b/roles/upload-forge/defaults/main.yaml
new file mode 100644
index 0000000..b732452
--- /dev/null
+++ b/roles/upload-forge/defaults/main.yaml
@@ -0,0 +1,2 @@
1---
2forge_url: "https://forgeapi.puppet.com"
diff --git a/roles/upload-forge/library/__init__.py b/roles/upload-forge/library/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/roles/upload-forge/library/__init__.py
diff --git a/roles/upload-forge/library/forge_upload.py b/roles/upload-forge/library/forge_upload.py
new file mode 100644
index 0000000..d477c8e
--- /dev/null
+++ b/roles/upload-forge/library/forge_upload.py
@@ -0,0 +1,182 @@
1#!/usr/bin/env python
2
3# Copyright (c) 2019 Binero
4# Author: Tobias Urdin <tobias.urdin@binero.se>
5#
6# Licensed under the Apache License, Version 2.0 (the "License"); you may
7# not use this file except in compliance with the License. You may obtain
8# a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15# License for the specific language governing permissions and limitations
16# under the License.
17
18
19ANSIBLE_METADATA = {
20 'metadata_version': '1.0',
21 'status': ['preview'],
22 'supported_by': 'community'
23}
24
25
26DOCUMENTATION = '''
27---
28module: forge_upload
29
30short_description: Uploads a puppet module tarball to a Forge server.
31
32description:
33 - "Uploads a puppet module tarball to a Forge server."
34
35options:
36 username:
37 description:
38 - The username to the Forge account
39 required: true
40 password:
41 description:
42 - The password to the Forge account
43 required: true
44 tarball:
45 description:
46 - The absolute path to the tarball of the puppet module
47 that should be uploaded
48 required: true
49 forgeapi:
50 description:
51 - This base url to the Forge server API, defaults to
52 https://forgeapi.puppet.com
53 required: false
54
55author:
56 - Tobias Urdin (@tobias-urdin)
57'''
58
59
60EXAMPLES = '''
61- name: Upload module
62 forge_upload:
63 username: 'myuser'
64 password: 'mypass'
65 tarball: '/home/myuser/test/pkg/myuser-test-0.1.0.tar.gz'
66'''
67
68
69RETURN = '''
70msg:
71 description: The output message from the module.
72'''
73
74
75from ansible.module_utils.basic import AnsibleModule # noqa
76import os # noqa
77import requests # noqa
78
79
80# Client ID and secret from puppet-blacksmith
81CLIENT_ID = 'b93eb708fd942cfc7b4ed71db6ce219b814954619dbe537ddfd208584e8cff8d'
82CLIENT_SECRET = '216648059ad4afec3e4d77bd9e67817c095b2dcf94cdec18ac3d00584f863180' # noqa
83
84FORGEAPI = 'https://forgeapi.puppet.com'
85
86
87def _get_url(forgeapi, path):
88 path = path[1:] if path.startswith('/') else path
89 return '%s/%s' % (forgeapi, path)
90
91
92def _forge_auth(forgeapi, username, password):
93 url = _get_url(forgeapi, '/oauth/token')
94 data = {
95 'client_id': CLIENT_ID,
96 'client_secret': CLIENT_SECRET,
97 'username': username,
98 'password': password,
99 'grant_type': 'password',
100 }
101 headers = {
102 'User-Agent': 'forge_upload-ansible-module/1.0',
103 }
104 response = requests.post(url, json=data, headers=headers)
105 return response
106
107
108def _forge_upload(forgeapi, token, tarball):
109 url = _get_url(forgeapi, '/v2/releases')
110 data = {
111 'file': open(tarball, 'rb').read(),
112 }
113 headers = {
114 'User-Agent': 'forge_upload-ansible-module/1.0',
115 'Authorization': 'Bearer %s' % token,
116 }
117 response = requests.post(url, files=data, headers=headers)
118 return response
119
120
121def run_module():
122 module_args = dict(
123 username=dict(type='str', required=True),
124 password=dict(type='str', required=True, no_log=True),
125 tarball=dict(type='str', required=True),
126 forgeapi=dict(type='str', default=FORGEAPI),
127 )
128
129 result = dict(
130 changed=False,
131 )
132
133 module = AnsibleModule(
134 argument_spec=module_args,
135 supports_check_mode=True
136 )
137
138 tarball = module.params['tarball']
139 if os.path.exists(tarball) is False:
140 module.fail_json(msg='Tarball %s does not exist' % tarball, **result)
141
142 resp = _forge_auth(module.params['forgeapi'],
143 module.params['username'],
144 module.params['password'])
145
146 if resp.status_code != 200:
147 msg = 'Forge API auth failed with code: %d' % resp.status_code
148 module.fail_json(msg=msg, **result)
149
150 if module.check_mode:
151 return result
152
153 auth = resp.json()
154 token = auth['access_token']
155
156 resp = _forge_upload(module.params['forgeapi'], token, tarball)
157
158 if resp.status_code == 409:
159 msg = 'Module %s already exists on Forge' % tarball
160 module.exit_json(msg=msg, **result)
161
162 if resp.status_code != 201:
163 try:
164 data = resp.json()
165 errors = ','.join(data['errors'])
166 except Exception:
167 errors = 'unknown'
168 msg = 'Forge API failed to upload tarball with code: %d errors: %s' % (
169 resp.status_code, errors)
170 module.fail_json(msg=msg, **result)
171
172 result['changed'] = True
173 module.exit_json(msg='Successfully uploaded tarball %s' % tarball,
174 **result)
175
176
177def main():
178 run_module()
179
180
181if __name__ == '__main__':
182 main()
diff --git a/roles/upload-forge/library/test_forge_upload.py b/roles/upload-forge/library/test_forge_upload.py
new file mode 100644
index 0000000..d3b0d5d
--- /dev/null
+++ b/roles/upload-forge/library/test_forge_upload.py
@@ -0,0 +1,28 @@
1#!/usr/bin/env python
2
3# Copyright (c) 2019 Binero
4# Author: Tobias Urdin <tobias.urdin@binero.se>
5#
6# Licensed under the Apache License, Version 2.0 (the "License"); you may
7# not use this file except in compliance with the License. You may obtain
8# a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15# License for the specific language governing permissions and limitations
16# under the License.
17
18
19import testtools
20from .forge_upload import _get_url
21
22
23class TestForgeUpload(testtools.TestCase):
24 def test_get_url(self):
25 base_url = 'https://forgeapi.puppet.com'
26 expected = 'https://forgeapi.puppet.com/test'
27 self.assertEqual(_get_url(base_url, '/test'), expected)
28 self.assertEqual(_get_url(base_url, 'test'), expected)
diff --git a/roles/upload-forge/tasks/main.yaml b/roles/upload-forge/tasks/main.yaml
new file mode 100644
index 0000000..4bae16a
--- /dev/null
+++ b/roles/upload-forge/tasks/main.yaml
@@ -0,0 +1,13 @@
1- name: Check required variables
2 assert:
3 that:
4 - "forge_username is defined"
5 - "forge_password is defined"
6 - "forge_tarball is defined"
7
8- name: Upload module to Forge
9 forge_upload:
10 username: "{{ forge_username }}"
11 password: "{{ forge_password }}"
12 tarball: "{{ forge_tarball }}"
13 forgeapi: "{{ forge_url }}"
diff --git a/roles/upload-puppetforge/README.rst b/roles/upload-puppetforge/README.rst
deleted file mode 100644
index 5eae36a..0000000
--- a/roles/upload-puppetforge/README.rst
+++ /dev/null
@@ -1,22 +0,0 @@
1Upload puppet module to Puppet Forge
2
3**Role Variables**
4
5 .. zuul:rolevar:: puppet_module_dir
6 :default: {{ zuul.project.src_dir }}
7
8 The folder where the puppet module code is that it can
9 switch folder to.
10
11 .. zuul:rolevar:: blacksmith_forge_url
12 :default: https://forgeapi.puppetlabs.com
13
14 The URL to the Puppet Forge API.
15
16 .. zuul:rolevar:: blacksmith_forge_username
17
18 Username to use to log in to Puppet Forge.
19
20 .. zuul:rolevar:: blacksmith_forge_password
21
22 Password to use to log in to Puppet Forge.
diff --git a/roles/upload-puppetforge/defaults/main.yaml b/roles/upload-puppetforge/defaults/main.yaml
deleted file mode 100644
index c93ad60..0000000
--- a/roles/upload-puppetforge/defaults/main.yaml
+++ /dev/null
@@ -1,3 +0,0 @@
1---
2puppet_module_dir: "{{ zuul.project.src_dir }}"
3blacksmith_forge_url: "https://forgeapi.puppetlabs.com"
diff --git a/roles/upload-puppetforge/tasks/main.yaml b/roles/upload-puppetforge/tasks/main.yaml
deleted file mode 100644
index 3003ce2..0000000
--- a/roles/upload-puppetforge/tasks/main.yaml
+++ /dev/null
@@ -1,52 +0,0 @@
1- name: Install ruby dependencies on RedHat/Suse based
2 package:
3 name:
4 - ruby-devel
5 - gcc-c++
6 - make
7 state: present
8 become: yes
9 when: ansible_os_family == "RedHat" or ansible_os_family == "Suse"
10
11- name: Install ruby dependencies on Debian based
12 package:
13 name:
14 - ruby-dev
15 - g++
16 - make
17 state: present
18 become: yes
19 when: ansible_os_family == "Debian"
20
21- name: Install required gems
22 gem:
23 name: "{{ item }}"
24 user_install: no
25 with_items:
26 - rake
27 - puppetlabs_spec_helper
28 - puppet-blacksmith
29 become: yes
30
31# NOTE(tobias.urdin): The build task is needed because puppet-blacksmith
32# doesn't provide a build task so it fails, we don't need one anyway since
33# we have already built the module before this role is called.
34- name: Install new Rakefile
35 copy:
36 content: |
37 namespace 'module' do
38 task 'build' do
39 end
40 end
41
42 require 'puppet_blacksmith/rake_tasks'
43 dest: "{{ puppet_module_dir }}/Rakefile"
44
45- name: Publish puppet module
46 command: "rake module:push"
47 args:
48 chdir: "{{ puppet_module_dir }}"
49 environment:
50 BLACKSMITH_FORGE_URL: "{{ blacksmith_forge_url }}"
51 BLACKSMITH_FORGE_USERNAME: "{{ blacksmith_forge_username }}"
52 BLACKSMITH_FORGE_PASSWORD: "{{ blacksmith_forge_password }}"