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

include cost models info on get/list provider response #1418

Merged
merged 1 commit into from
Nov 22, 2019
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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