Skip to content

Commit

Permalink
feat: cluster list fetchers and cluster resource fetcher
Browse files Browse the repository at this point in the history
  • Loading branch information
tmishina committed Aug 16, 2020
1 parent 3270484 commit 5d97cf3
Show file tree
Hide file tree
Showing 7 changed files with 474 additions and 2 deletions.
58 changes: 58 additions & 0 deletions arboretum/common/errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# -*- 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.
"""Common error classes."""


class CommandExecutionError(RuntimeError):
"""Represents error at executing command."""

def __init__(self, cmd, stdout, stderr, returncode):
"""Initialize an instance.
Initialize an instance with the return values of the command.
"""
self.__cmd = cmd
self.__stdout = stdout
self.__stderr = stderr
self.__returncode = returncode

def __str__(self):
"""Get information about the command line and its result."""
return (
f'Error running command: {self.cmd}\n'
f'returncode: {self.returncode}\n'
f'stdout: {self.stdout}\n'
f'stderr: {self.stderr}'
)

@property
def cmd(self):
"""Get command line text."""
return self.__cmd

@property
def stdout(self):
"""Get standard out text of the command."""
return self.__stdout

@property
def stderr(self):
"""Get standard error text of the command."""
return self.__stderr

@property
def returncode(self):
"""Get return code of the command."""
return self.__returncode
26 changes: 26 additions & 0 deletions arboretum/common/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
# limitations under the License.
"""Common utility functions."""

import subprocess

from arboretum.common.errors import CommandExecutionError

from compliance.evidence import DAY, HOUR


Expand All @@ -34,3 +38,25 @@ def parse_seconds(seconds):
formatted.append(f'{q} {unit}')
seconds = r
return ', '.join(formatted)


def run_command(cmd, secrets=None):
"""Run commands in a system."""
if type(cmd) == str:
cmd = cmd.split(' ')
p = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True
)
stdout, stderr = p.communicate()

if p.returncode != 0:
secrets = secrets or []
for s in secrets:
cmd = cmd.replace(s, '***')
stdout = stdout.replace(s, '***')
stderr = stderr.replace(s, '***')
raise CommandExecutionError(cmd, stdout, stderr, p.returncode)
return stdout
54 changes: 53 additions & 1 deletion arboretum/ibm_cloud/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,62 @@ how to include the fetchers and checks from this library in your downstream proj

## Fetchers

Fetchers coming soon...
### Cluster List

* Class: [ClusterListFetcher][fetch-cluster-list]
* Purpose: Write the list of IBM Cloud clusters to the evidence locker.
* Behavior: Log in to IBM Cloud using `ibmcloud login` command, and save the result of `ibmcloud cs cluster ls` command.
* Expected configuration elements:
* org.ibm_cloud.cluster_list.config
* List of objects representing the IKS accounts
* Each object must have the following values
* `account` - a list containing names identifying the IKS account, this will map to an IAM token provided in the credentials file
* Expected configuration example:
```json
{
"org": {
"ibm_cloud": {
"cluster_list": {
"config": {
"account": ["myaccount1"]
}
}
}
}
}
```
* <a name="expected_credentials"></a>Expected credentials:
* `ibm_cloud` credentials with read/view permissions are needed for this fetcher to successfully
retrieve the evidence.
* One IKS API key is required per account specified in the configuration. See above. Each account provided in the configuration must preceed `_api_key`. For example, if we have specified accounts "acct_a", "acct_b", and "acct_c" your configuration should look like:

```ini
[ibm_cloud]
acct_a_api_key=your-iks-api-key-for-acct-a
acct_b_api_key=your-iks-api-key-for-acct-b
acct_c_api_key=your-iks-api-key-for-acct-c
```

* Expected Travis environment variable settings to generate credentials:
* `IBM_CLOUD_ACCT_A_API_KEY`
* `IBM_CLOUD_ACCT_B_API_KEY`
* `IBM_CLOUD_ACCT_C_API_KEY`

* NOTE: [API Keys can be generated using the ibmcloud CLI][ic-api-key-create]. E.g.

```sh
ibmcloud iam api-key-create your-iks-api-key-for-acct-x
```
* Import statement:

```python
from arboretum.ibm_cloud.fetchers.fetch_cluster_list import ClusterListFetcher
```

## Checks

Checks coming soon...

[usage]: https://github.com/ComplianceAsCode/auditree-arboretum#usage
[ic-api-key-create]: https://cloud.ibm.com/docs/cli/reference/ibmcloud?topic=cloud-cli-ibmcloud_commands_iam#ibmcloud_iam_api_key_create
[fetch-cluster-list]: https://github.com/ComplianceAsCode/auditree-arboretum/blob/main/arboretum/ibm_cloud/fetchers/fetch_cluster_list.py
70 changes: 70 additions & 0 deletions arboretum/ibm_cloud/fetchers/fetch_cluster_list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# -*- 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.
"""IKS cluster list fetcher."""

import json

from arboretum.common.errors import CommandExecutionError
from arboretum.common.utils import run_command

from compliance.evidence import store_raw_evidence
from compliance.fetch import ComplianceFetcher


class ClusterListFetcher(ComplianceFetcher):
"""Fetch the list of IBM Cloud clusters."""

@classmethod
def setUpClass(cls):
"""Initialize the fetcher object with configuration settings."""
cls.logger = cls.locker.logger.getChild(
'ibm_cloud.cluster_list_fetcher'
)
return cls

@store_raw_evidence('ibm_cloud/cluster_list.json')
def fetch_cluster_list(self):
"""Fetch IBM Cloud cluster list."""
for account in self.config.get(
'org.ibm_cloud.cluster_list.config.account'):
return json.dumps({account: self._get_cluster_list(account)})

def _get_cluster_list(self, account):

# get credential for the account
api_key = getattr(self.config.creds['ibm_cloud'], account + '_api_key')

# login
run_command(
f'ibmcloud login --no-region --apikey {api_key}',
secrets=[api_key]
)

# get cluster list
cluster_list = None
cmd = 'ibmcloud cs cluster ls --json'
try:
cluster_list = run_command(cmd)
except CommandExecutionError as e:
if e.returncode == 2: # "2" means no plugin error
self.logger.warning(
'Failed to execute "ibmcloud cs" command'
' - trying to install the cs plugin'
)
run_command('ibmcloud plugin install kubernetes-service')
cluster_list = run_command(cmd)
else:
raise e
return json.loads(cluster_list)
76 changes: 75 additions & 1 deletion arboretum/kubernetes/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,84 @@ how to include the fetchers and checks from this library in your downstream proj

## Fetchers

Fetchers coming soon...
### 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 `raw/kubernetes/cluster_list.json`.
* Expected configuration elements:
* org.kubernetes.cluster_list.config.bom
* List of target kubernetes clusters (see example below)
* Expected configuration example:
```json
{
"org": {
"kubernetes": {
"cluster_list": {
"config": {
"bom": [
{
"account": "ibmcloud_myaccount",
"name": "mycluster-free",
"kubeconfig": "/home/myaccount/.kube/mycluster-free.kubeconfig",
"type": "kubernetes"
}
]
}
}
}
}
}
```
* Expected credentials:
* A kubeconfig file specified in org.kube.cluster_list.config.bom[].kubeconfig of config file must be a valid kubeconfig file.
* Import statement:
```python
from arboretum.kubernetes.fetchers.fetch_cluster_list import ClusterListFetcher
```

### Cluster Resource

* Class: [ClusterResourceFetcher][fetch-cluster-resource]
* Purpose: Write the resources of clusters to the evidence locker.
* Behavior: Read a cluster list from `raw/CATEGORY/cluster_list.json` where `CATEGORY` is the category name specified in configuration, and fetch resources from the clusters. Fetch target resource types can be specified in config file.
* Expected configuration elements:
* org.kubernetes.cluster_resource.config
* `cluster_list_types`: cluster list types (same as category names) - for example, specify `kubernetes` if you want to read the cluster list by [ClusterListFetcher of kubernetes][fetch-cluster-list], and specify `ibm_cloud` if you want to read the cluster list by [ClusterListFetcher of ibm_cloud][fetch-cluster-list-ibmcloud].
* `target_resource_types`: list of target resource types (default: [`node`, `configmap`])
* Expected configuration example:
```json
{
"org": {
"kubernetes": {
"cluster_resource": {
"config": {
"cluster_list_types": [
"kubernetes", "ibm_cloud"
],
"target_resource_types": [
"node"
]
}
}
}
}
}
```
* Expected credentials:
* Credentials (including kubeconfig file) are required for the clusters specified in the configuration. See documents of cluster list fetchers ([ClusterListFetcher of kubernetes][fetch-cluster-list], [ClusterListFetcher of ibm_cloud][fetch-cluster-list-ibmcloud]) because the cluster resource fetcher also use the credentials for the list fetchers.
* Import statement:

```python
from arboretum.kubernetes.fetchers.fetch_cluster_resource import ClusterResourceFetcher
```


## Checks

Checks coming soon...

[usage]: https://github.com/ComplianceAsCode/auditree-arboretum#usage
[fetch-cluster-list]: https://github.com/ComplianceAsCode/auditree-arboretum/blob/main/arboretum/kubernetes/fetchers/fetch_cluster_list.py
[fetch-cluster-list-ibmcloud]: https://github.com/ComplianceAsCode/auditree-arboretum/blob/main/arboretum/ibm_cloud/fetchers/fetch_cluster_list.py
[fetch-cluster-resource]: https://github.com/ComplianceAsCode/auditree-arboretum/blob/main/arboretum/kubernetes/fetchers/fetch_cluster_resource.py
37 changes: 37 additions & 0 deletions arboretum/kubernetes/fetchers/fetch_cluster_list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# -*- 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.
"""Fetch cluster list by using other providers' fetch_cluster_list function."""
import json

from compliance.evidence import store_raw_evidence
from compliance.fetch import ComplianceFetcher


class ClusterListFetcher(ComplianceFetcher):
"""Fetch BOM (Bill of Materials) as cluster list."""

@classmethod
def setUpClass(cls):
"""Initialize the fetcher object with configuration settings."""
cls.logger = cls.locker.logger.getChild(
'kubernetes.cluster_list_fetcher'
)
return cls

@store_raw_evidence('kubernetes/cluster_list.json')
def fetch_cluster_list(self):
"""Fetch BOM (Bill of Materials) as cluster list."""
bom = self.config.get('org.kubernetes.cluster_list.config.bom')
return json.dumps(bom)
Loading

0 comments on commit 5d97cf3

Please sign in to comment.