Browse Source

Merge "Rework upload-forge role to use module"

changes/70/636870/1
Zuul 3 months ago
parent
commit
a06ff1ce19

+ 24
- 0
roles/upload-forge/README.rst View File

@@ -0,0 +1,24 @@
1
+Upload puppet module tarball to a Forge server
2
+
3
+This role requires the python requests module to be
4
+installed 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.

+ 0
- 0
roles/upload-forge/__init__.py View File


+ 2
- 0
roles/upload-forge/defaults/main.yaml View File

@@ -0,0 +1,2 @@
1
+---
2
+forge_url: "https://forgeapi.puppet.com"

+ 0
- 0
roles/upload-forge/library/__init__.py View File


+ 182
- 0
roles/upload-forge/library/forge_upload.py View File

@@ -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
+
19
+ANSIBLE_METADATA = {
20
+    'metadata_version': '1.0',
21
+    'status': ['preview'],
22
+    'supported_by': 'community'
23
+}
24
+
25
+
26
+DOCUMENTATION = '''
27
+---
28
+module: forge_upload
29
+
30
+short_description: Uploads a puppet module tarball to a Forge server.
31
+
32
+description:
33
+    - "Uploads a puppet module tarball to a Forge server."
34
+
35
+options:
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
+
55
+author:
56
+    - Tobias Urdin (@tobias-urdin)
57
+'''
58
+
59
+
60
+EXAMPLES = '''
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
+
69
+RETURN = '''
70
+msg:
71
+    description: The output message from the module.
72
+'''
73
+
74
+
75
+from ansible.module_utils.basic import AnsibleModule  # noqa
76
+import os  # noqa
77
+import requests  # noqa
78
+
79
+
80
+# Client ID and secret from puppet-blacksmith
81
+CLIENT_ID = 'b93eb708fd942cfc7b4ed71db6ce219b814954619dbe537ddfd208584e8cff8d'
82
+CLIENT_SECRET = '216648059ad4afec3e4d77bd9e67817c095b2dcf94cdec18ac3d00584f863180'  # noqa
83
+
84
+FORGEAPI = 'https://forgeapi.puppet.com'
85
+
86
+
87
+def _get_url(forgeapi, path):
88
+    path = path[1:] if path.startswith('/') else path
89
+    return '%s/%s' % (forgeapi, path)
90
+
91
+
92
+def _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
+
108
+def _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
+
121
+def 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
+
177
+def main():
178
+    run_module()
179
+
180
+
181
+if __name__ == '__main__':
182
+    main()

+ 28
- 0
roles/upload-forge/library/test_forge_upload.py View File

@@ -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
+
19
+import testtools
20
+from .forge_upload import _get_url
21
+
22
+
23
+class 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)

+ 13
- 0
roles/upload-forge/tasks/main.yaml View File

@@ -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 }}"

+ 0
- 22
roles/upload-puppetforge/README.rst View File

@@ -1,22 +0,0 @@
1
-Upload 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.

+ 0
- 3
roles/upload-puppetforge/defaults/main.yaml View File

@@ -1,3 +0,0 @@
1
----
2
-puppet_module_dir: "{{ zuul.project.src_dir }}"
3
-blacksmith_forge_url: "https://forgeapi.puppetlabs.com"

+ 0
- 52
roles/upload-puppetforge/tasks/main.yaml View File

@@ -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 }}"

Loading…
Cancel
Save