Browse Source

Merge pull request #9901 from kwoodson/remove_publishing

Removing azure publishing tooling.
Kenny Woodson 6 years ago
parent
commit
ae44a79d27

+ 0 - 53
playbooks/azure/openshift-cluster/create_and_publish_offer.md

@@ -1,53 +0,0 @@
-# Create and publish offer
-
-Note:  This document is not intended for general consumption.
-
-
-This document outlines the process in which to publish an image to the cloudpartner.azure.com portal.
-
-# Publish image
-
-The steps to build the image are as follows:
-
-## Step 1:
-
-Build the Openshift image using the build_node_image.yml playbook.  Once this playbook completes it should
-produce a storage blob that points to the image.  This blob exists inside of the resourcegroup named images,
-storage accounts named openshiftimages, and the container named, images.
-
-```
-$ ansible-playbook build_node_image.yml
-```
-
-## Step 2:
-
-This step performs the following work:
-- generates a storage blob url
-- generates a sas url for the storage container
-- a cancel of any current operations on the offer will be called (in case of any updates)
-- if an offer exists, the current offer will be fetched and updated
-- if an offer ! exist, the offer will be created
-- a publish is called on the offer
-
-```
-$ ansible-playbook  create_and_publish_offer.yml -e @publishingvars.yml
-```
-
-Example publishingvars.yml
-```
-openshift_azure_container: images
-openshift_azure_storage_account: openshiftimages
-image_name: rhel7-3.9-201805211419
-openshift_azure_image_publish_emails:
-- your.name@email.com
-openshift_azure_templ_allowed_subscriptions:
-- <subcription id1>
-- <subcription id2>
-openshift_azure_templ_restricted_audience_manual_entries:
-- type: subscriptionId
-  id: <subcription id1>
-  description: <description1>
-- type: subscriptionId
-  id: <subcription id2>
-  description: <description2>
-```

+ 0 - 98
playbooks/azure/openshift-cluster/create_and_publish_offer.yml

@@ -1,98 +0,0 @@
----
-- hosts: localhost
-  gather_facts: no
-  tasks:
-  - name: ensure libraries are available
-    import_role:
-      name: lib_utils
-
-  - name: set expire date
-    set_fact:
-      sas_expire: "{{ lookup('pipe', 'date -d \"14 days\" +\"%Y-%m-%dT%H:%M:00Z\"') }}"
-      disk: "{{ image_name }}.vhd"
-
-  - name: fetch the image for the tags
-    command: |
-      az image list --resource-group images  -o json --query "[?name=='{{ image_name }}'].tags"
-    register: imagetags
-
-  - name: generate storage blob url
-    command: |
-      az storage blob url -c {{ openshift_azure_container }} --account-name {{ openshift_azure_storage_account }} -n {{ disk }} --output=json
-    register: bloburl
-
-  - name: generate sas url for the container
-    command: |
-      az storage container generate-sas --name {{ openshift_azure_container }} --account-name {{ openshift_azure_storage_account }} --permissions rl --expiry {{ sas_expire }} --output=json
-    register: sasurl
-
-  - name: set the sas URLS
-    set_fact:
-      openshift_azure_sas_url: "{{ bloburl.stdout|from_json + '?' + sasurl.stdout|from_json }}"
-
-  - name: set the image tags
-    set_fact:
-      image_tags: "{{ (imagetags.stdout | from_json)[0] }}"
-
-  - name: set the image_type
-    set_fact:
-      image_type: "{{ 'rhel' if 'rhel' in image_tags.base_image else 'centos' }}"
-      image_x: "{{ image_tags.openshift.split('.')[0] }}"
-      image_y: "{{ image_tags.openshift.split('.')[1] }}"
-      image_z: "{{ image_tags.openshift.split('.')[2] }}"
-      image_vm_images: |-
-        { "{{ image_x }}{{ image_y }}.{{ image_z }}.{{ (image_name | regex_search('([0-9]{12})'))[:8] }}": {"osVhdUrl": "{{ openshift_azure_sas_url }}" } }
-
-  - name: fetch the current offer and update the versions
-    oo_azure_rm_publish_image_facts:
-      offer: osa
-    register: offerout
-
-  - when:
-    - offerout['status_code'] != 404
-    block:
-    - debug:
-        msg: "{{ offerout }}"
-        verbosity: 1
-
-    - debug:
-        msg: "{{ offerout['data']['definition']['plans'][0]['microsoft-azure-virtualmachines.vmImages'] }}"
-        verbosity: 1
-
-    - debug:
-        msg: "{{ lookup('template', 'offer.yml.j2') }}"
-        verbosity: 1
-
-    - name: bring along the previous offer versions and combine with incoming
-      yedit:
-        content: "{{ lookup('template', 'offer.yml.j2') }}"
-        key: "definition#plans[0]#microsoft-azure-virtualmachines.vmImages#{{ item.key }}"
-        value: "{{ item.value }}"
-        separator: '#'
-      with_dict: "{{ offerout['data']['definition']['plans'][0]['microsoft-azure-virtualmachines.vmImages'] }}"
-      when:
-      - "'data' in offerout"
-      - "'definition' in offerout['data']"
-      register: yeditout
-
-    - debug:
-        msg: "{{ yeditout }}"
-        verbosity: 1
-
-    # this cancel operation returns a 202 whether it cancelled or not.
-    - name: cancel publish operation
-      oo_azure_rm_publish_image:
-        offer: osa
-        state: cancel_op
-
-  - name: create|update an offer in cloudpartner portal
-    oo_azure_rm_publish_image:
-      offer: osa
-      offer_data: "{{ (lookup('template', 'offer.yml.j2') | from_yaml) if 'skipped' in yeditout and yeditout.skipped or not yeditout.changed else yeditout.results[0].result[0].edit }}"
-      force: True
-
-  - name: publish this offer
-    oo_azure_rm_publish_image:
-      state: publish
-      offer: osa
-      emails: "{{ openshift_azure_image_publish_emails }}"

+ 0 - 13
playbooks/azure/openshift-cluster/group_vars/all/image_publish.yml

@@ -1,13 +0,0 @@
----
-azure_image_publish:
-  rhel:
-    templ_plan_id: osa_{{ image_x }}{{ image_y }}
-    templ_sku_title: Openshift {{ image_x }}.{{ image_y }} on Azure
-    templ_os_type: Red Hat Enterprise Linux
-    templ_os: Red Hat Enterprise Linux 7
-
-  centos:
-    templ_plan_id: origin_{{ image_x }}{{ image_y }}
-    templ_sku_title: Openshift Origin {{ image_x }}.{{ image_y }} on Azure
-    templ_os_type: CentOS
-    templ_os: CentOS 7

+ 0 - 61
playbooks/azure/openshift-cluster/templates/offer.yml.j2

@@ -1,61 +0,0 @@
-offerTypeId: microsoft-azure-virtualmachines
-publisherId: redhat
-id: osa
-definition:
-  displayText: OpenShift on Azure
-  offer:
-    microsoft-azure-marketplace-testdrive.enabled: false
-    microsoft-azure-marketplace-testdrive.videos: []
-    microsoft-azure-marketplace.title: OpenShift on Azure
-    microsoft-azure-marketplace.summary: OpenShift on Azure
-    microsoft-azure-marketplace.longSummary: OpenShift on Azure
-    microsoft-azure-marketplace.description: OpenShift on Azure
-    microsoft-azure-marketplace.offerMarketingUrlIdentifier: osa
-    microsoft-azure-marketplace.allowedSubscriptions: {{ openshift_azure_templ_allowed_subscriptions | to_json }}
-    microsoft-azure-marketplace.usefulLinks: []
-    microsoft-azure-marketplace.categories: [appInfrastructure, businessApplication, devService, web]
-    microsoft-azure-marketplace.smallLogo: "https://publishingstoredm.blob.core.windows.net/prodcontent/D6191_publishers_redhat/origin:2Dacsengine/62aad711-f499-461f-b74c-f4f31586b0f5.png?sv=2014-02-14&sr=b&sig=ewX%2F9aAkgG3EMLzBMPjyHuEzRDhvs8TmOsNWYPRXwg8%3D&se=2020-04-13T21%3A02%3A53Z&sp=r"
-    microsoft-azure-marketplace.mediumLogo: "https://publishingstoredm.blob.core.windows.net/prodcontent/D6191_publishers_redhat/origin:2Dacsengine/df6bed86-6891-4558-848f-be236ec981d5.png?sv=2014-02-14&sr=b&sig=QzYeJ6qdoMoUTCeaFycpqrBpO0Lnr7upy%2FQCArYvhno%3D&se=2020-04-13T21%3A02%3A53Z&sp=r"
-    microsoft-azure-marketplace.largeLogo: "https://publishingstoredm.blob.core.windows.net/prodcontent/D6191_publishers_redhat/origin:2Dacsengine/b486c038-bf6a-4881-a5c6-d3619bb27884.png?sv=2014-02-14&sr=b&sig=1BetRsM%2BJch9zhYaagMcYwkD7Lantdm%2FInHUQ7LfJZ0%3D&se=2020-04-13T21%3A02%3A53Z&sp=r"
-    microsoft-azure-marketplace.wideLogo: "https://publishingstoredm.blob.core.windows.net/prodcontent/D6191_publishers_redhat/origin:2Dacsengine/141969bb-300b-47a3-ae9f-2573af3e8720.png?sv=2014-02-14&sr=b&sig=DnhC36RA3rGNOqByHrP8dvIBmgCHQr95%2B5PqJe9D1qk%3D&se=2020-04-13T21%3A02%3A53Z&sp=r"
-    microsoft-azure-marketplace.screenshots: []
-    microsoft-azure-marketplace.videos: []
-    microsoft-azure-marketplace.leadDestination: None
-    microsoft-azure-marketplace.tableLeadConfiguration: {}
-    microsoft-azure-marketplace.blobLeadConfiguration: {}
-    microsoft-azure-marketplace.salesForceLeadConfiguration: {}
-    microsoft-azure-marketplace.crmLeadConfiguration: {}
-    microsoft-azure-marketplace.httpsEndpointLeadConfiguration: {}
-    microsoft-azure-marketplace.marketoLeadConfiguration: {}
-    microsoft-azure-marketplace.privacyURL: "https://www.redhat.com/en/about/privacy-policy"
-    microsoft-azure-marketplace.termsOfUse: TODO
-    microsoft-azure-marketplace.engineeringContactEmail: support@redhat.com
-    microsoft-azure-marketplace.engineeringContactName: Red Hat Support
-    microsoft-azure-marketplace.engineeringContactPhone: 888-467-3342
-    microsoft-azure-marketplace.supportContactEmail: support@redhat.com
-    microsoft-azure-marketplace.supportContactName: Red Hat Support
-    microsoft-azure-marketplace.supportContactPhone: 888-467-3342
-    microsoft-azure-marketplace.publicAzureSupportUrl: ''
-    microsoft-azure-marketplace.fairfaxSupportUrl: ''
-  plans:
-  - planId: {{ azure_image_publish[image_type].templ_plan_id }}
-    microsoft-azure-virtualmachines.skuTitle: {{ azure_image_publish[image_type].templ_sku_title }}
-    microsoft-azure-virtualmachines.skuSummary: {{ azure_image_publish[image_type].templ_sku_title }}
-    microsoft-azure-virtualmachines.skuDescription: {{ azure_image_publish[image_type].templ_sku_title }}
-    microsoft-azure-virtualmachines.hideSKUForSolutionTemplate: true
-    microsoft-azure-virtualmachines.cloudAvailability: [PublicAzure]
-    microsoft-azure-virtualmachines.certificationsFairfax: []
-    restrictedAudience:
-      manualEntries: {{ openshift_azure_templ_restricted_audience_manual_entries | to_json }}
-    virtualMachinePricing: {isByol: true, freeTrialDurationInMonths: 0}
-    microsoft-azure-virtualmachines.operatingSystemFamily: Linux
-    microsoft-azure-virtualmachines.osType: {{ azure_image_publish[image_type].templ_os_type }}
-    microsoft-azure-virtualmachines.operationSystem: {{ azure_image_publish[image_type].templ_os }}
-    microsoft-azure-virtualmachines.recommendedVMSizes: [d2s-standard-v3, d4s-standard-v3]
-    microsoft-azure-virtualmachines.openPorts: []
-    microsoft-azure-virtualmachines.vmImages: {{ image_vm_images | to_json }}
-    regions: [DZ, AR, AU, AT, BH, BY, BE, BR, BG, CA, CL, CO, CR, HR, CY, CZ, DK,
-      DO, EC, EG, SV, EE, FI, FR, DE, GR, GT, HK, HU, IS, IN, ID, IE, IL, IT, JP,
-      JO, KZ, KE, KR, KW, LV, LI, LT, LU, MK, MY, MT, MX, ME, MA, NL, NZ, NG, 'NO',
-      OM, PK, PA, PY, PE, PH, PL, PT, PR, QA, RO, RU, SA, RS, SG, SK, SI, ZA, ES,
-      LK, SE, CH, TW, TH, TT, TN, TR, UA, AE, GB, US, UY, VE]

+ 0 - 290
roles/lib_utils/library/oo_azure_rm_publish_image.py

@@ -1,290 +0,0 @@
-#!/usr/bin/env python
-# pylint: disable=missing-docstring
-# Copyright 2018 Red Hat, Inc. and/or its affiliates
-# and other contributors as indicated by the @author tags.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#    http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-from __future__ import print_function  # noqa: F401
-# import httplib
-import json
-import os
-import time
-import requests
-
-from ansible.module_utils.basic import AnsibleModule
-
-
-class AzurePublisherException(Exception):
-    '''Exception class for AzurePublisher'''
-    pass
-
-
-class AzurePublisher(object):
-    '''Python class to represent the Azure Publishing portal https://cloudpartner.azure.com'''
-
-    # pylint: disable=too-many-arguments
-    def __init__(self,
-                 publisher_id,
-                 client_info,
-                 ssl_verify=True,
-                 api_version='2017-10-31',
-                 debug=False):
-        '''
-          :publisher_id: string of the publisher id
-          :client_info: a dict containing the client_id, client_secret to get an access_token
-        '''
-        self._azure_server = 'https://cloudpartner.azure.com/api/publishers/{}'.format(publisher_id)
-        self.client_info = client_info
-        self.ssl_verify = ssl_verify
-        self.api_version = 'api-version={}'.format(api_version)
-        self.debug = debug
-        # if self.debug:
-        # import httplib
-        # httplib.HTTPSConnection.debuglevel = 1
-        # httplib.HTTPConnection.debuglevel = 1
-
-        self._access_token = None
-
-    @property
-    def server(self):
-        '''property for  server url'''
-        return self._azure_server
-
-    @property
-    def token(self):
-        '''property for the access_token
-            curl --data-urlencode "client_id=$AZURE_CLIENT_ID" \
-                --data-urlencode "client_secret=$AZURE_CLIENT_SECRET" \
-                --data-urlencode "grant_type=client_credentials" \
-                --data-urlencode "resource=https://cloudpartner.azure.com" \
-                https://login.microsoftonline.com/$AZURE_TENANT_ID/oauth2/token
-        '''
-        if self._access_token is None:
-            url = 'https://login.microsoftonline.com/{}/oauth2/token'.format(self.client_info['tenant_id'])
-            data = {
-                'client_id': {self.client_info['client_id']},
-                'client_secret': self.client_info['client_secret'],
-                'grant_type': 'client_credentials',
-                'resource': 'https://cloudpartner.azure.com'
-            }
-
-            results = AzurePublisher.request('POST', url, data, {})
-            jres = results.json()
-            self._access_token = jres['access_token']
-
-        return self._access_token
-
-    def get_offers(self, offer=None, version=None, slot='preview'):
-        ''' fetch all offers by publisherid '''
-        url = '/offers'
-
-        if offer is not None:
-            url += '/{}'.format(offer)
-            if version is not None:
-                url += '/versions/{}'.format(version)
-            if slot == 'preview':
-                url += '/slot/{}'.format(slot)
-
-        url += '?{}'.format(self.api_version)
-
-        return self.prepare_action(url)
-
-    def get_operations(self, offer, operation=None, status=None):
-        ''' create or modify an offer '''
-        url = '/offers/{0}/submissions'.format(offer)
-
-        if operation is not None:
-            url += '/operations/{0}'.format(operation)
-
-        if not url.endswith('/'):
-            url += '/'
-
-        url += '?{0}'.format(self.api_version)
-
-        if status is not None:
-            url += '&status={0}'.format(status)
-
-        return self.prepare_action(url, 'GET')
-
-    def cancel_operation(self, offer):
-        ''' create or modify an offer '''
-        url = '/offers/{0}/cancel?{1}'.format(offer, self.api_version)
-
-        return self.prepare_action(url, 'POST')
-
-    def publish(self, offer, emails):
-        ''' publish an offer '''
-        url = '/offers/{0}/publish?{1}'.format(offer, self.api_version)
-
-        data = {
-            'metadata': {
-                'notification-emails': ','.join(emails),
-            }
-        }
-
-        return self.prepare_action(url, 'POST', data=data)
-
-    def go_live(self, offer):
-        ''' create or modify an offer '''
-        url = '/offers/{0}/golive?{1}'.format(offer, self.api_version)
-
-        return self.prepare_action(url, 'POST')
-
-    def create_or_modify_offer(self, offer, data=None, modify=False):
-        ''' create or modify an offer '''
-        url = '/offers/{0}?{1}'.format(offer, self.api_version)
-
-        headers = None
-
-        if modify:
-            headers = {
-                'If-Match': '*',
-            }
-
-        return self.prepare_action(url, 'PUT', data=data, add_headers=headers)
-
-    def prepare_action(self, url, action='GET', data=None, add_headers=None):
-        '''perform the http request
-
-           :action: string of either GET|POST
-        '''
-        headers = {
-            'Content-Type': 'application/json',
-            'Accept': 'application/json',
-            'Authorization': 'Bearer {}'.format(self.token)
-        }
-
-        if add_headers is not None:
-            headers.update(add_headers)
-
-        if data is None:
-            data = ''
-        else:
-            data = json.dumps(data)
-
-        return AzurePublisher.request(action.upper(), self.server + url, data, headers)
-
-    def cancel_and_wait_for_operation(self, params):
-        '''cancel the current publish operation and wait for operation to complete'''
-
-        # cancel the publish operation
-        self.cancel_operation(offer=params['offer'])
-
-        # we need to wait here for 'submissionState' to move to 'canceled'
-        while True:
-            # fetch operations
-            ops = self.get_operations(params['offer'])
-            if self.debug:
-                print(ops.json())
-            if ops.json()[0]['submissionState'] == 'canceled':
-                break
-
-            time.sleep(5)
-
-        return ops
-
-    def manage_offer(self, params):
-        ''' handle creating or modifying offers'''
-        # fetch the offer to verify it exists:
-        results = self.get_offers(offer=params['offer'])
-
-        if results.status_code == 200 and params['force']:
-            return self.create_or_modify_offer(offer=params['offer'], data=params['offer_data'], modify=True)
-
-        return self.create_or_modify_offer(offer=params['offer'], data=params['offer_data'])
-
-    @staticmethod
-    def request(action, url, data=None, headers=None, ssl_verify=True):
-        req = requests.Request(action.upper(), url, data=data, headers=headers)
-
-        session = requests.Session()
-        req_prep = session.prepare_request(req)
-        response = session.send(req_prep, verify=ssl_verify)
-
-        return response
-
-    @staticmethod
-    def run_ansible(params):
-        '''perform the ansible operations'''
-        client_info = {
-            'tenant_id': params['tenant_id'],
-            'client_id': params['client_id'],
-            'client_secret': params['client_secret']}
-
-        apc = AzurePublisher(params['publisher'],
-                             client_info,
-                             debug=params['debug'])
-
-        if params['state'] == 'offer':
-            results = apc.manage_offer(params)
-        elif params['state'] == 'publish':
-            results = apc.publish(offer=params['offer'], emails=params['emails'])
-            results.json = lambda: ''
-        elif params['state'] == 'cancel_op':
-            results = apc.cancel_and_wait_for_operation(params)
-        elif params['state'] == 'go_live':
-            results = apc.go_live(offer=params['offer'])
-        else:
-            raise AzurePublisherException('Unsupported query type: {}'.format(params['state']))
-
-        changed = False
-
-        if results.status_code in [200, 201, 202]:
-            changed = True
-
-        return {'data': results.json(), 'changed': changed, 'status_code': results.status_code}
-
-
-# pylint: disable=too-many-branches
-def main():
-    ''' ansible oc module for secrets '''
-
-    module = AnsibleModule(
-        argument_spec=dict(
-            state=dict(default='offer', choices=['offer', 'cancel_op', 'go_live', 'publish']),
-            force=dict(default=False, type='bool'),
-            publisher=dict(default='redhat', type='str'),
-            debug=dict(default=False, type='bool'),
-            tenant_id=dict(default=os.environ.get('AZURE_TENANT_ID'), type='str'),
-            client_id=dict(default=os.environ.get('AZURE_CLIENT_ID'), type='str'),
-            client_secret=dict(default=os.environ.get('AZURE_CLIENT_SECRET'), type='str'),
-            offer=dict(default=None, type='str'),
-            offer_data=dict(default=None, type='dict'),
-            emails=dict(default=None, type='list'),
-        ),
-
-        required_if=[
-            ["state", "offer", ["offer_data"]],
-        ],
-    )
-
-    # Verify we recieved either a valid key or edits with valid keys when receiving a src file.
-    # A valid key being not None or not ''.
-    if (module.params['tenant_id'] is None or module.params['client_id'] is None or
-            module.params['client_secret'] is None):
-        return module.fail_json(**{'failed': True,
-                                   'msg': 'Please specify tenant_id, client_id, and client_secret'})
-
-    rval = AzurePublisher.run_ansible(module.params)
-
-    if int(rval['status_code']) >= 300:
-        rval['msg'] = 'Failed. status_code {}'.format(rval['status_code'])
-        return module.fail_json(**rval)
-
-    return module.exit_json(**rval)
-
-
-if __name__ == '__main__':
-    main()

+ 0 - 242
roles/lib_utils/library/oo_azure_rm_publish_image_facts.py

@@ -1,242 +0,0 @@
-#!/usr/bin/env python
-# pylint: disable=missing-docstring
-# Copyright 2018 Red Hat, Inc. and/or its affiliates
-# and other contributors as indicated by the @author tags.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#    http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-from __future__ import print_function  # noqa: F401
-# import httplib
-import json
-import os
-import requests
-
-from ansible.module_utils.basic import AnsibleModule
-
-
-class AzurePublisherException(Exception):
-    '''Exception class for AzurePublisher'''
-    pass
-
-
-class AzurePublisher(object):
-    '''Python class to represent the Azure Publishing portal https://cloudpartner.azure.com'''
-
-    # pylint: disable=too-many-arguments
-    def __init__(self,
-                 publisher_id,
-                 client_info,
-                 ssl_verify=True,
-                 api_version='2017-10-31',
-                 debug=False):
-        '''
-          :publisher_id: string of the publisher id
-          :client_info: a dict containing the client_id, client_secret to get an access_token
-        '''
-        self._azure_server = 'https://cloudpartner.azure.com/api/publishers/{}'.format(publisher_id)
-        self.client_info = client_info
-        self.ssl_verify = ssl_verify
-        self.api_version = 'api-version={}'.format(api_version)
-        self.debug = debug
-        # if self.debug:
-        # httplib.HTTPSConnection.debuglevel = 1
-        # httplib.HTTPConnection.debuglevel = 1
-
-        self._access_token = None
-
-    @property
-    def server(self):
-        '''property for  server url'''
-        return self._azure_server
-
-    @property
-    def token(self):
-        '''property for the access_token
-            curl --data-urlencode "client_id=$AZURE_CLIENT_ID" \
-                --data-urlencode "client_secret=$AZURE_CLIENT_SECRET" \
-                --data-urlencode "grant_type=client_credentials" \
-                --data-urlencode "resource=https://cloudpartner.azure.com" \
-                https://login.microsoftonline.com/$AZURE_TENANT_ID/oauth2/token
-        '''
-        if self._access_token is None:
-            url = 'https://login.microsoftonline.com/{}/oauth2/token'.format(self.client_info['tenant_id'])
-            data = {
-                'client_id': {self.client_info['client_id']},
-                'client_secret': self.client_info['client_secret'],
-                'grant_type': 'client_credentials',
-                'resource': 'https://cloudpartner.azure.com'
-            }
-
-            results = AzurePublisher.request('POST', url, data, {})
-            jres = results.json()
-            self._access_token = jres['access_token']
-
-        return self._access_token
-
-    def get_offers(self, offer=None, version=None, slot=''):
-        ''' fetch all offers by publisherid '''
-        url = '/offers'
-
-        if offer is not None:
-            url += '/{}'.format(offer)
-            if version is not None:
-                url += '/versions/{}'.format(version)
-            if slot != '':
-                url += '/slot/{}'.format(slot)
-
-        url += '?{}'.format(self.api_version)
-
-        return self.prepare_action(url)
-
-    def get_operations(self, offer, operation=None, status=None):
-        ''' create or modify an offer '''
-        url = '/offers/{0}/submissions'.format(offer)
-
-        if operation is not None:
-            url += '/operations/{0}'.format(operation)
-
-        if not url.endswith('/'):
-            url += '/'
-
-        url += '?{0}'.format(self.api_version)
-
-        if status is not None:
-            url += '&status={0}'.format(status)
-
-        return self.prepare_action(url, 'GET')
-
-    def cancel_operation(self, offer):
-        ''' create or modify an offer '''
-        url = '/offers/{0}/cancel?{1}'.format(offer, self.api_version)
-
-        return self.prepare_action(url, 'POST')
-
-    def go_live(self, offer):
-        ''' create or modify an offer '''
-        url = '/offers/{0}/golive?{1}'.format(offer, self.api_version)
-
-        return self.prepare_action(url, 'POST')
-
-    def create_or_modify_offer(self, offer, data=None, modify=False):
-        ''' create or modify an offer '''
-        url = '/offers/{0}?{1}'.format(offer, self.api_version)
-
-        headers = None
-
-        if modify:
-            headers = {
-                'If-Match': '*',
-            }
-
-        return self.prepare_action(url, 'PUT', data=data, add_headers=headers)
-
-    def prepare_action(self, url, action='GET', data=None, add_headers=None):
-        '''perform the http request
-
-           :action: string of either GET|POST
-        '''
-        headers = {
-            'Content-Type': 'application/json',
-            'Accept': 'application/json',
-            'Authorization': 'Bearer {}'.format(self.token)
-        }
-
-        if add_headers is not None:
-            headers.update(add_headers)
-
-        if data is None:
-            data = ''
-        else:
-            data = json.dumps(data)
-
-        return AzurePublisher.request(action.upper(), self.server + url, data, headers)
-
-    def manage_offer(self, params):
-        ''' handle creating or modifying offers'''
-        # fetch the offer to verify it exists:
-        results = self.get_offers(offer=params['offer'])
-
-        if results.status_code == 200 and params['force']:
-            return self.create_or_modify_offer(offer=params['offer'], data=params['offer_data'], modify=True)
-
-        return self.create_or_modify_offer(offer=params['offer'], data=params['offer_data'])
-
-    @staticmethod
-    def request(action, url, data=None, headers=None, ssl_verify=True):
-        req = requests.Request(action.upper(), url, data=data, headers=headers)
-
-        session = requests.Session()
-        req_prep = session.prepare_request(req)
-        response = session.send(req_prep, verify=ssl_verify)
-
-        return response
-
-    @staticmethod
-    def run_ansible(params):
-        '''perform the ansible operations'''
-        client_info = {
-            'tenant_id': params['tenant_id'],
-            'client_id': params['client_id'],
-            'client_secret': params['client_secret']}
-
-        apc = AzurePublisher(params['publisher'],
-                             client_info,
-                             debug=params['debug'])
-
-        if params['query'] == 'offer':
-            results = apc.get_offers(offer=params['offer'])
-        elif params['query'] == 'operation':
-            results = apc.get_operations(offer=params['offer'], operation=params['operation'], status=params['status'])
-        else:
-            raise AzurePublisherException('Unsupported query type: {}'.format(params['query']))
-
-        return {'data': results.json(), 'status_code': results.status_code}
-
-
-def main():
-    ''' ansible oc module for secrets '''
-
-    module = AnsibleModule(
-        argument_spec=dict(
-            query=dict(default='offer', choices=['offer', 'operation']),
-            publisher=dict(default='redhat', type='str'),
-            debug=dict(default=False, type='bool'),
-            tenant_id=dict(default=os.environ.get('AZURE_TENANT_ID'), type='str'),
-            client_id=dict(default=os.environ.get('AZURE_CLIENT_ID'), type='str'),
-            client_secret=dict(default=os.environ.get('AZURE_CLIENT_SECRET'), type='str'),
-            offer=dict(default=None, type='str'),
-            operation=dict(default=None, type='str'),
-            status=dict(default=None, type='str'),
-        ),
-    )
-
-    # Verify we recieved either a valid key or edits with valid keys when receiving a src file.
-    # A valid key being not None or not ''.
-    if (module.params['tenant_id'] is None or module.params['client_id'] is None or
-            module.params['client_secret'] is None):
-        return module.fail_json(**{'failed': True,
-                                   'msg': 'Please specify tenant_id, client_id, and client_secret'})
-
-    rval = AzurePublisher.run_ansible(module.params)
-    if int(rval['status_code']) == 404:
-        rval['msg'] = 'Offer does not exist.'
-    elif int(rval['status_code']) >= 300:
-        rval['msg'] = 'Error.'
-        return module.fail_json(**rval)
-
-    return module.exit_json(**rval)
-
-
-if __name__ == '__main__':
-    main()