Skip to content

Commit

Permalink
include cost models info on get/list provider response
Browse files Browse the repository at this point in the history
  • Loading branch information
infinitewarp committed Nov 22, 2019
1 parent 89596ee commit 22aae46
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 9 deletions.
20 changes: 19 additions & 1 deletion docs/source/specs/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -3110,6 +3110,24 @@
"active": {
"type": "boolean",
"description": "Flag to indicate when the provider is configured correctly"
},
"cost_models": {
"type": "array",
"description": "List of cost model name and UUIDs associated with this provider.",
"items": {
"type": "object",
"properties": {
"uuid": {
"type": "string",
"format": "uuid",
"example": "D823A725-DC10-496A-AF08-12533E4F8FE4"
},
"name": {
"type": "string",
"example": "My Great Cost Model"
}
}
}
}
}
}
Expand Down Expand Up @@ -5286,4 +5304,4 @@
}
}
}
}
}
7 changes: 7 additions & 0 deletions koku/api/provider/provider_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,13 @@ def provider_statistics(self, tenant=None):

return provider_stats

def get_cost_models(self, tenant):
"""Get the cost models associated with this provider."""
with tenant_context(tenant):
cost_models_map = CostModelMap.objects.filter(provider_uuid=self._uuid)
cost_models = [m.cost_model for m in cost_models_map]
return cost_models

def update(self, request):
"""Check if provider is a sources model."""
if self.sources_model and not request.headers.get('Sources-Client'):
Expand Down
95 changes: 87 additions & 8 deletions koku/api/provider/test/tests_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@
"""Test the Provider views."""
import copy
from unittest.mock import patch
from uuid import uuid4
from uuid import UUID, uuid4

import faker
from django.urls import reverse
from rest_framework import serializers
from rest_framework import status
Expand All @@ -31,6 +32,7 @@
from providers.provider_access import ProviderAccessor

fields = ['name', 'type', 'authentication', 'billing_source']
FAKE = faker.Faker()


class ProviderViewTest(IamTestCase):
Expand Down Expand Up @@ -61,6 +63,27 @@ def create_provider(self, bucket_name, iam_arn, headers=None):
client = APIClient()
return client.post(url, data=provider, format='json', **req_headers)

def create_cost_model(self, provider_uuids, headers=None):
"""Create a cost model and return response."""
req_headers = self.headers
if headers:
req_headers = headers
cost_model_data = {
'name': FAKE.catch_phrase(),
'source_type': Provider.PROVIDER_AWS,
'description': FAKE.paragraph(),
'rates': [],
'markup': {
'value': FAKE.pyint() % 100, 'unit': 'percent'
},
'provider_uuids': [UUID(p) for p in provider_uuids]
}
url = reverse('costmodels-list')
with patch.object(ProviderAccessor, 'cost_usage_source_ready', returns=True):
client = APIClient()
result = client.post(url, data=cost_model_data, format='json', **req_headers)
return result

def test_create_aws_with_no_provider_resource_name(self):
"""Test missing provider_resource_name returns 400."""
req_headers = self.headers
Expand Down Expand Up @@ -186,17 +209,47 @@ def test_create_provider_shared_arn_bucket_fails(self):
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

def test_list_provider(self):
"""Test list providers."""
request_context = self._create_request_context(self.create_mock_customer_data(),
self._create_user_data(),
create_tenant=True)
headers = request_context['request'].META
"""
Test list providers.
This test asserts a combination of factors. We create two providers,
but each provider belongs to a different user. The first provider has
a cost model, but the second does not. We assert that each provider is
listed only for its respective user's request context.
"""
# Define the context for the second user; this must happen FIRST for reasons
# currently unknown. If you call this after creating the first provider, DB
# exceptions are raised (django.db.utils.OperationalError: cannot ALTER TABLE)
request_context = self._create_request_context(
self.create_mock_customer_data(),
self._create_user_data(),
create_tenant=True)
alternate_headers = request_context['request'].META

# Create a provider with a cost model.
iam_arn1 = 'arn:aws:s3:::my_s3_bucket'
bucket_name1 = 'my_s3_bucket'
first_create_response = self.create_provider(bucket_name1, iam_arn1)
first_provider_result = first_create_response.json()
first_provider_uuid = first_provider_result.get('uuid')
create_cost_model_response = self.create_cost_model([first_provider_uuid])
cost_model_result = create_cost_model_response.json()
self.assertIsNotNone(cost_model_result['uuid'])
self.assertIsNotNone(cost_model_result['name'])
expected_cost_model_info = {
'name': cost_model_result['name'], 'uuid': cost_model_result['uuid']
}

# Create a second provider but for a different user.
iam_arn2 = 'arn:aws:s3:::a_s3_bucket'
bucket_name2 = 'a_s3_bucket'
self.create_provider(bucket_name1, iam_arn1)
self.create_provider(bucket_name2, iam_arn2, headers)
second_create_response = self.create_provider(
bucket_name2, iam_arn2, alternate_headers
)
second_provider_result = second_create_response.json()
second_provider_uuid = second_provider_result.get('uuid')

# List and expect it to contain only the first provider with cost model.
url = reverse('provider-list')
client = APIClient()
response = client.get(url, **self.headers)
Expand All @@ -205,17 +258,40 @@ def test_list_provider(self):
results = json_result.get('data')
self.assertIsNotNone(results)
self.assertEqual(len(results), 1)
self.assertEqual(results[0]['uuid'], first_provider_uuid)
self.assertEqual(results[0].get('infrastructure'), 'Unknown')
self.assertEqual(results[0].get('stats'), {})
self.assertEqual(len(results[0]['cost_models']), 1)
self.assertEqual(results[0]['cost_models'][0], expected_cost_model_info)

# List as the different user and expect the second provider with no cost model.
response = client.get(url, **alternate_headers)
self.assertEqual(response.status_code, status.HTTP_200_OK)
json_result = response.json()
results = json_result.get('data')
self.assertIsNotNone(results)
self.assertEqual(len(results), 1)
self.assertEqual(results[0]['uuid'], second_provider_uuid)
self.assertEqual(len(results[0]['cost_models']), 0)

def test_get_provider(self):
"""Test get a provider."""
# Set up all the data for this test.
iam_arn = 'arn:aws:s3:::my_s3_bucket'
bucket_name = 'my_s3_bucket'
create_response = self.create_provider(bucket_name, iam_arn, )
provider_result = create_response.json()
provider_uuid = provider_result.get('uuid')
self.assertIsNotNone(provider_uuid)
create_cost_model_response = self.create_cost_model([provider_uuid])
cost_model_result = create_cost_model_response.json()
self.assertIsNotNone(cost_model_result['uuid'])
self.assertIsNotNone(cost_model_result['name'])
expected_cost_model_info = {
'name': cost_model_result['name'], 'uuid': cost_model_result['uuid']
}

# Call the API for testing the results.
url = reverse('provider-detail', args=[provider_uuid])
client = APIClient()
response = client.get(url, **self.headers)
Expand All @@ -226,6 +302,9 @@ def test_get_provider(self):
self.assertEqual(uuid, provider_uuid)
self.assertEqual(json_result.get('stats'), {})
self.assertEqual(json_result.get('infrastructure'), 'Unknown')
cost_models = json_result.get('cost_models')
self.assertEqual(len(cost_models), 1)
self.assertEqual(cost_models[0], expected_cost_model_info)

def test_filter_providers_by_name_contains(self):
"""Test that providers that contain name appear."""
Expand Down
8 changes: 8 additions & 0 deletions koku/api/provider/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ def list(self, request, *args, **kwargs):
tenant = get_tenant(request.user)
provider['stats'] = manager.provider_statistics(tenant)
provider['infrastructure'] = manager.get_infrastructure_name(tenant)
provider['cost_models'] = [
{'name': model.name, 'uuid': model.uuid}
for model in manager.get_cost_models(tenant)
]
return response

@never_cache
Expand All @@ -164,6 +168,10 @@ def retrieve(self, request, *args, **kwargs):
manager = ProviderManager(kwargs['uuid'])
response.data['infrastructure'] = manager.get_infrastructure_name(tenant)
response.data['stats'] = manager.provider_statistics(tenant)
response.data['cost_models'] = [
{'name': model.name, 'uuid': model.uuid}
for model in manager.get_cost_models(tenant)
]
return response

@never_cache
Expand Down

0 comments on commit 22aae46

Please sign in to comment.