Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add metadata update for CA, peer, ordering nodes #50

Merged
merged 1 commit into from
Apr 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions plugins/module_utils/consoles.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,29 @@ def delete_ca(self, id):
continue
return self.handle_error('Failed to delete certificate authority', e)

def update_metadata_ca(self, id, data):
self._ensure_loggedin()
url = urllib.parse.urljoin(self.api_base_url, f'./components/fabric-ca/{id}')

headers = {
'Accepts': 'application/json',
'Content-Type': 'application/json',
'Authorization': self.authorization
}
data = json.dumps(data)
for attempt in range(1, self.retries + 1):
try:
self.module.json_log({'msg': 'attempting to submit update to certificate authority', 'data': data, 'url': url, 'attempt': attempt, 'api_timeout': self.api_timeout})
response = open_url(url, data, headers, 'PUT', validate_certs=False, timeout=self.api_timeout, follow_redirects='all')
result = json.load(response)
self.module.json_log({'msg': 'submitted update to certificate authority', 'result': result})
return result
except Exception as e:
self.module.json_log({'msg': 'failed to submit update to certificate authority', 'error': str(e)})
if self.should_retry_error(e, attempt):
continue
return self.handle_error('Failed to submit update to certificate authority', e)

def action_ca(self, id, data):
self._ensure_loggedin()
url = urllib.parse.urljoin(self.api_base_url, f'./kubernetes/components/fabric-ca/{id}/actions')
Expand Down Expand Up @@ -483,6 +506,29 @@ def _update_peer(self, id, data, ignore_warnings):
continue
return self.handle_error('Failed to update peer', e)

def update_metadata_peer(self, id, data):
self._ensure_loggedin()
url = urllib.parse.urljoin(self.api_base_url, f'./components/fabric-peer/{id}')

headers = {
'Accepts': 'application/json',
'Content-Type': 'application/json',
'Authorization': self.authorization
}
data = json.dumps(data)
for attempt in range(1, self.retries + 1):
try:
self.module.json_log({'msg': 'attempting to submit update to peer', 'data': data, 'url': url, 'attempt': attempt, 'api_timeout': self.api_timeout})
response = open_url(url, data, headers, 'PUT', validate_certs=False, timeout=self.api_timeout, follow_redirects='all')
result = json.load(response)
self.module.json_log({'msg': 'submitted update to peer', 'result': result})
return result
except Exception as e:
self.module.json_log({'msg': 'failed to submit update to peer', 'error': str(e)})
if self.should_retry_error(e, attempt):
continue
return self.handle_error('Failed to submit update to peer', e)

def action_peer(self, id, data):
self._ensure_loggedin()
url = urllib.parse.urljoin(self.api_base_url, f'./kubernetes/components/fabric-peer/{id}/actions')
Expand Down Expand Up @@ -779,6 +825,29 @@ def delete_ordering_service_node(self, id):
continue
return self.handle_error('Failed to delete ordering service node', e)

def update_metadata_ordering_service_node(self, id, data):
self._ensure_loggedin()
url = urllib.parse.urljoin(self.api_base_url, f'./components/fabric-orderer/{id}')

headers = {
'Accepts': 'application/json',
'Content-Type': 'application/json',
'Authorization': self.authorization
}
data = json.dumps(data)
for attempt in range(1, self.retries + 1):
try:
self.module.json_log({'msg': 'attempting to submit update to ordering service node', 'data': data, 'url': url, 'attempt': attempt, 'api_timeout': self.api_timeout})
response = open_url(url, data, headers, 'PUT', validate_certs=False, timeout=self.api_timeout, follow_redirects='all')
result = json.load(response)
self.module.json_log({'msg': 'submitted update to ordering service node', 'result': result})
return result
except Exception as e:
self.module.json_log({'msg': 'failed to submit update to ordering service node', 'error': str(e)})
if self.should_retry_error(e, attempt):
continue
return self.handle_error('Failed to submit update to ordering service node', e)

def action_ordering_service_node(self, id, data):
self._ensure_loggedin()
url = urllib.parse.urljoin(self.api_base_url, f'./kubernetes/components/fabric-orderer/{id}/actions')
Expand Down
207 changes: 207 additions & 0 deletions plugins/modules/certificate_authority_metadata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
#!/usr/bin/python
#
# SPDX-License-Identifier: Apache-2.0
#

from __future__ import absolute_import, division, print_function
__metaclass__ = type

from ..module_utils.module import BlockchainModule
from ..module_utils.certificate_authorities import CertificateAuthority
from ..module_utils.utils import get_console

from ansible.module_utils._text import to_native

ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}

DOCUMENTATION = '''
---
module: certificate_authority_metadata
short_description: Update information about a Hyperledger Fabric certificate authority metadata
description:
- Updates metadata for a certificate authority.
- This module works with the IBM Support for Hyperledger Fabric software or the Hyperledger Fabric
Open Source Stack running in a Red Hat OpenShift or Kubernetes cluster.
author: Chris Elder
options:
api_endpoint:
description:
- The URL for the the Fabric operations console.
type: str
required: true
api_authtype:
description:
- C(basic) - Authenticate to the the Fabric operations console using basic authentication.
You must provide both a valid API key using I(api_key) and API secret using I(api_secret).
type: str
required: true
api_key:
description:
- The API key for the the Fabric operations console.
type: str
required: true
api_secret:
description:
- The API secret for the the Fabric operations console.
- Only required when I(api_authtype) is C(basic).
type: str
api_timeout:
description:
- The timeout, in seconds, to use when interacting with the the Fabric operations console.
type: int
default: 60
name:
description:
- The name of the certificate authority.
required: true
preferred_url:
description:
- Preferred URL style for the components.
- os is used the open source style with standard ports (443)
- legacy is the software as a service style URLs
required: false
wait_timeout:
description:
- The timeout, in seconds, to wait until the certificate authority is available.
type: int
default: 60
notes: []
requirements: []
'''

EXAMPLES = '''
- name: Get certificate authority
hyperledger.fabric_ansible_collection.certificate_authority_action:
api_endpoint: https://console.example.org:32000
api_authtype: basic
api_key: xxxxxxxx
api_secret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
name: Org1 CA
preferred_url: os
'''

RETURN = '''
---
exists:
description:
- True if the certificate authority exists, false otherwise.
returned: always
type: boolean
certificate_authority:
description:
- The certificate authority.
returned: if certificate authority exists
type: dict
contains:
name:
description:
- The name of the certificate authority.
type: str
sample: Org1 CA
api_url:
description:
- The URL for the API of the certificate authority.
type: str
sample: https://org1ca-api.example.org:32000
operations_url:
description:
- The URL for the operations service of the certificate authority.
type: str
sample: https://org1ca-operations.example.org:32000
ca_url:
description:
- The URL for the API of the certificate authority.
type: str
sample: https://org1ca-api.example.org:32000
ca_name:
description:
- The certificate authority name to use for enrollment requests.
type: str
sample: ca
tlsca_name:
description:
- The certificate authority name to use for TLS enrollment requests.
type: str
sample: tlsca
location:
description:
- The location of the certificate authority.
type: str
sample: ibmcloud
pem:
description:
- The TLS certificate chain for the certificate authority.
- The TLS certificate chain is returned as a base64 encoded PEM.
type: str
sample: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0t...
tls_cert:
description:
- The TLS certificate chain for the certificate authority.
- The TLS certificate chain is returned as a base64 encoded PEM.
type: str
sample: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0t...
'''


def main():

# Create the module.
argument_spec = dict(
api_endpoint=dict(type='str', required=True),
api_authtype=dict(type='str', required=True, choices=['ibmcloud', 'basic']),
api_key=dict(type='str', required=True, no_log=True),
api_secret=dict(type='str', no_log=True),
api_timeout=dict(type='int', default=60),
api_token_endpoint=dict(type='str', default='https://iam.cloud.ibm.com/identity/token'),
name=dict(type='str', required=True),
preferred_url=dict(type='str', choices=['os', 'legacy']),
wait_timeout=dict(type='int', default=600)
)
required_if = [
('api_authtype', 'basic', ['api_secret'])
]
module = BlockchainModule(argument_spec=argument_spec, supports_check_mode=True, required_if=required_if)

# Ensure all exceptions are caught.
try:

timeout = module.params['wait_timeout']

# Log in to the console.
console = get_console(module)

changed = False

# Determine if the certificate authority exists.
certificate_authority = console.get_component_by_display_name('fabric-ca', module.params['name'], deployment_attrs='included')

# If it doesn't exist, return now.
if certificate_authority is None:
return module.exit_json(exists=False)

if module.params['preferred_url'] is None:
return module.exit_json(exists=False)

certificate_authority_metadata_update = dict(
preferred_url=module.params['preferred_url']
)

certificate_authority = console.update_metadata_ca(certificate_authority['id'], certificate_authority_metadata_update)
changed = True

certificate_authority = CertificateAuthority.from_json(console.extract_ca_info(certificate_authority))
timeout = module.params['wait_timeout']
certificate_authority.wait_for(timeout)

# Return certificate authority information.
module.exit_json(changed=changed, certificate_authority=certificate_authority.to_json())

# Notify Ansible of the exception.
except Exception as e:
module.fail_json(msg=to_native(e))


if __name__ == '__main__':
main()
34 changes: 32 additions & 2 deletions plugins/modules/channel_consenter.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,15 @@
- You can also pass a dict, which must match the result format of one
of the M(ordering_service_node_info) or M(ordering_service_node) modules.
type: raw
required: true
updated_ordering_service_node:
description:
- The updated ordering service node to use as a consenter for this channel.
- This option must used if the host or port is to be updated in the consenter set.
- This must be passed as a dict, which must match the result format of one
of the M(ordering_service_node_info) or M(ordering_service_node) modules.
type: raw
required: false
notes: []
requirements: []
'''
Expand Down Expand Up @@ -127,7 +136,8 @@ def main():
api_timeout=dict(type='int', default=60),
api_token_endpoint=dict(type='str', default='https://iam.cloud.ibm.com/identity/token'),
path=dict(type='str', required=True),
ordering_service_node=dict(type='raw', required=True)
ordering_service_node=dict(type='raw', required=True),
updated_ordering_service_node=dict(type='raw')
)
required_if = [
('api_authtype', 'basic', ['api_secret'])
Expand Down Expand Up @@ -181,6 +191,26 @@ def main():
break
consenter_exists = consenter_idx > -1

# use the updated_ordering_service_node if the host or port in the consenter is changing
# the existing host and port is needed to determine the consenter to be updated
if module.params['updated_ordering_service_node'] is not None:

# Get the ordering service node.
ordering_service_node = get_ordering_service_node_by_module(console, module, 'updated_ordering_service_node')

# Build the expected consenter based on the new ordering node.
parsed_api_url = urllib.parse.urlparse(ordering_service_node.api_url)
host = parsed_api_url.hostname
port = parsed_api_url.port or 443
client_tls_cert = ordering_service_node.client_tls_cert or ordering_service_node.tls_cert
server_tls_cert = ordering_service_node.server_tls_cert or ordering_service_node.tls_cert
expected_consenter = dict(
host=host,
port=port,
client_tls_cert=client_tls_cert,
server_tls_cert=server_tls_cert,
)

# Handle the desired state appropriately.
state = module.params['state']
if state == 'present' and not consenter_exists:
Expand Down Expand Up @@ -227,7 +257,7 @@ def main():
config_proto = json_to_proto('common.Config', config_json)
with open(path, 'wb') as file:
file.write(config_proto)
module.exit_json(changed=True)
module.exit_json(changed=True, original_config_json=original_config_json, updated_config_json=config_json)

# Notify Ansible of the exception.
except Exception as e:
Expand Down
Loading