Conversation
f06035d to
3596647
Compare
alfinkel
left a comment
There was a problem hiding this comment.
- I didn't review the READMEs. Saving that for later.
- You need to add unit tests for the
iam_ibmmodule.
f98931f to
b6453f9
Compare
|
@alfinkel I added a unit test for Test in a local environment is passed as follows. Is there any solution for this issue? |
Could that be mocked @tmishina ? |
https://docs.python.org/3/library/unittest.mock.html you know the good/bad conditions, so should be able to mock the behviour.Not perfect, but gets around needing a live IAM to talk to, and can be extended in the future if responses change. |
|
@drsm79 thank you, I realized that my knowledge about Python should be update to Python3...l will update the code with unittest.mock. |
259553c to
ad7542f
Compare
|
updated with using Now the test successfully finishes in the status checks. |
alfinkel
left a comment
There was a problem hiding this comment.
We're getting closer @tmishina...
A few code tweaks, a rewrite of the unit tests, and you still need to remove the BOM fetcher and docs. See: https://github.com/ComplianceAsCode/auditree-arboretum/pull/25/files#r481124180
|
@alfinkel update the usage of mock; does this usage of mock make sense for you? Other changes include applyin your suggestions and deleting |
alfinkel
left a comment
There was a problem hiding this comment.
I copied this iam_ibm.py and changed it to iam_ibm_utils.py with a few small modifications and submitted it for PR #26. I also added tests for get_tokens. Once #26 is merged you can refresh your branch and retest the fetcher to make sure it still works and just delete your iam_ibm.py and test_iam_ibm.py.
| def setUpClass(cls): | ||
| """Initialize the fetcher object with configuration settings.""" | ||
| headers = {'Accept': 'application/json'} | ||
| cls.session('https://containers.cloud.ibm.com', **headers) |
There was a problem hiding this comment.
Move 'https://containers.cloud.ibm.com' to ibm_constants.py (in #26) and use the constant value here instead. Suggest:
# IBM Cloud containers API base URL
IC_CONTAINERS_BASE_URL = 'https://containers.cloud.ibm.com'after IAM_API_KEY_GRANT_TYPE in constants.py.
arboretum/kubernetes/README.md
Outdated
| ### Cluster List | ||
|
|
||
| * Class: [ClusterListFetcher][fetch-cluster-list] | ||
| * Purpose: Write the list of kubernetes clusters to the evidence locker. | ||
| * Behavior: Read BOM (Bill of Materials) data in config file and write it into evidence locker. | ||
| * Configuration elements: | ||
| * `org.kubernetes.cluster_list.bom` | ||
| * Required | ||
| * List where each element contains `account`, `name`, `kubeconfig` and `type`. `kubeconfig` is a path to a kubeconfig file for the cluster. Useer can specify any value for other fields to distinguish the cluster from other clusters. | ||
| * Example configuration: | ||
|
|
||
| ```json | ||
| { | ||
| "org": { | ||
| "kubernetes": { | ||
| "cluster_list": { | ||
| "bom": [ | ||
| { | ||
| "account": "myaccount", | ||
| "name": "mycluster-free", | ||
| "kubeconfig": "/home/myaccount/.kube/mycluster-free.kubeconfig", | ||
| "type": "kubernetes" | ||
| } | ||
| ] | ||
| } | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| * Required credentials: | ||
| * A valid kubeconfig file must exist at the path specified in `org.kubernetes.cluster_list.bom[].kubeconfig`. | ||
| * Import statement: | ||
|
|
||
| ```python | ||
| from arboretum.kubernetes.fetchers.fetch_cluster_list import ClusterListFetcher | ||
| ``` |
There was a problem hiding this comment.
Should return this to original Fetchers coming soon...
arboretum/common/iam_ibm.py
Outdated
| # -*- mode:python; coding:utf-8 -*- | ||
| # Copyright (c) 2020 IBM Corp. All rights reserved. | ||
| # | ||
| # 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. | ||
| """Utility module for IBM Cloud IAM.""" | ||
| import requests | ||
|
|
||
|
|
||
| def get_tokens(api_key): | ||
| """ | ||
| Get tokens (access token and refresh token) by api_key. | ||
|
|
||
| see https://cloud.ibm.com/apidocs/iam-identity-token-api | ||
| """ | ||
| headers = { | ||
| 'Content-Type': 'application/x-www-form-urlencoded', | ||
| 'Accept': 'application/json' | ||
| } | ||
| grant_type = 'urn:ibm:params:oauth:grant-type:apikey' | ||
| resp = requests.post( | ||
| 'https://iam.cloud.ibm.com/identity/token', | ||
| headers=headers, | ||
| auth=('bx', 'bx'), | ||
| data=f'grant_type={grant_type}&apikey={api_key}' | ||
| ) | ||
| resp.raise_for_status() | ||
| access_token = resp.json()['access_token'] | ||
| refresh_token = resp.json()['refresh_token'] | ||
| return access_token, refresh_token |
There was a problem hiding this comment.
Replace this with iam_ibm_utils.py from #26 once it is merged.
test/test_iam_ibm.py
Outdated
| # -*- mode:python; coding:utf-8 -*- | ||
| # Copyright (c) 2020 IBM Corp. All rights reserved. | ||
| # | ||
| # 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. | ||
| """Arboretim IBM Cloud IAM utitity module tests.""" | ||
| import unittest | ||
| from unittest import mock | ||
|
|
||
| from arboretum.common.iam_ibm import get_tokens | ||
|
|
||
| from requests import HTTPError, Response | ||
|
|
||
|
|
||
| def _mock_requests_post(*args, **kwargs): | ||
| data = kwargs['data'] | ||
| kw = 'apikey=' | ||
| idx = data.find(kw) | ||
| api_key = data[idx + len(kw):] | ||
|
|
||
| def json_function(**kwargs): | ||
| return { | ||
| 'access_token': 'yI6ICJodHRwOi8vc2VydmVyLmV4YW1y' | ||
| 'I6ICJodHRwOi8vc2VydmVyLmV4YW1', | ||
| 'refresh_token': 'yI6ICJodHRwOi8vc2VydmVyLmV4YW1y' | ||
| 'I6ICJodHRwOi8vc2VydmVyLmV4YW1' | ||
| } | ||
|
|
||
| resp = Response() | ||
| if len(api_key) > len('yI6ICJodHRwOi8vc2VydmVyLmV4YW1'): | ||
| resp.status_code = 200 | ||
| resp.json = json_function | ||
| else: | ||
| raise HTTPError() # status_code is automatically set to 400. | ||
| return resp | ||
|
|
||
|
|
||
| class IamIbmTest(unittest.TestCase): | ||
| """Arboretum IBM Cloud IAM functions tests.""" | ||
|
|
||
| @mock.patch('requests.post') | ||
| def test_get_tokens_success(self, mock_post): | ||
| """Ensure that an API call with valid API key returns valid tokens.""" | ||
| resp = Response() | ||
| resp.json = lambda **kwargs: { | ||
| 'access_token': 'yI6ICJodHRwOi8vc2VydmVyLmV4YW1y' | ||
| 'I6ICJodHRwOi8vc2VydmVyLmV4YW1', | ||
| 'refresh_token': 'yI6ICJodHRwOi8vc2VydmVyLmV4YW1y' | ||
| 'I6ICJodHRwOi8vc2VydmVyLmV4YW1' | ||
| } | ||
| resp.status_code = 200 | ||
| mock_post.return_value = resp | ||
| api_key = 'yI6ICJodHRwOi8vc2VydmVyLmV4YW1xxxx' | ||
| access_token, refresh_token = get_tokens(api_key) | ||
|
|
||
| # spec of access_token and refresh_token | ||
| # https://cloud.ibm.com/docs/mobilefoundation?topic=mobilefoundation-basic_authentication | ||
| # the doc above says tokens are longer than its examples. | ||
| self.assertIsInstance(access_token, str) | ||
| self.assertGreater( | ||
| len(access_token), len('yI6ICJodHRwOi8vc2VydmVyLmV4YW1') | ||
| ) | ||
| self.assertIsInstance(refresh_token, str) | ||
| self.assertGreater( | ||
| len(refresh_token), len('yI7ICasdsdJodHRwOi8vc2Vashnneh') | ||
| ) | ||
|
|
||
| # ensure requests.post() is called as expected | ||
| mock_post.assert_called_once_with( | ||
| 'https://iam.cloud.ibm.com/identity/token', | ||
| headers={ | ||
| 'Content-Type': 'application/x-www-form-urlencoded', | ||
| 'Accept': 'application/json' | ||
| }, | ||
| auth=('bx', 'bx'), | ||
| data='grant_type=urn:ibm:params:oauth:grant-type:' | ||
| f'apikey&apikey={api_key}' | ||
| ) |
eace20a to
22b929e
Compare
|
@alfinkel applied your comments and perform |
ce27cfd to
ad6b261
Compare
|
@alfinkel Thank you, I applied your suggestion with additional import statements. Also, I performed |
ad6b261 to
8666710
Compare
alfinkel
left a comment
There was a problem hiding this comment.
+1 - I'll merge and then add a follow up PR to update the missing CHANGES.md entry and version bump and then make a release.
PR #15 is split into two PRs; cluster list fetchers are included in this PR, and cluster resource fetchers will be included in another PR.
What
This pull request provides a feature to fetch list of kubernetes clusters per account (see #9 in details).
For vanilla kubernetes clusters, a BOM (Bill of Materials) described in a config file will be stored into an evidence locker. For other types of clusters, a list of clusters is fetched by invoking API of each cloud service provider. This PR includes a fetcher for IBM Cloud.
Why
The resources in a kubernetes cluster contain various types of evidences; for example,
specof Pods represents configuration of applications, ConfigMap contains the configuration for the kubernetes cluster itself, and therefore fetching the resources of a kubernetes cluster is important capability for compliance evidence validation of kubernetes clusters.To fetch resources from a kubernetes cluster, a list of the target clusters is required. This PR includes cluster list fetchers, and its output will be used by cluster resource fetchers (will be provided in another future PR).
How
kube/fetchers/fetch_cluster_list.py: copy BOM (Bill of Materials) specified in an auditree config file into an evidence lockeribm_cloud/fetchers/fetch_cluster_list.py: fetch the list of clusters managed by IBM Cloud (IBM Kubernetes Service or IKS, and Red Hat Openshift Kubernetes Services or ROKS) by invoking IBM Cloud API.Test
kubernetesandibm_cloud) against the IBM Cloud clusters (both IKS and ROKS)were passed
Context