feat(aci): provide container details page (#4037)

* feat(aci): show basic details

* feat(aci): style container details page

* fix(aci): fix container ip

* feat(aci): provide functions to get single aci resource

* feat(aci): show readable data

* feat(aci): style container instance
pull/4076/head
Chaim Lev-Ari 2020-07-21 00:08:20 +03:00 committed by GitHub
parent 4b97cf738e
commit 53cddeb283
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 225 additions and 6 deletions

View File

@ -38,6 +38,16 @@ angular.module('portainer.azure', ['portainer.app']).config([
},
};
var containerInstance = {
name: 'azure.containerinstances.container',
url: '/:id',
views: {
'content@': {
component: 'containerInstanceDetails',
},
},
};
var containerInstanceCreation = {
name: 'azure.containerinstances.new',
url: '/new/',
@ -62,6 +72,7 @@ angular.module('portainer.azure', ['portainer.app']).config([
$stateRegistryProvider.register(azure);
$stateRegistryProvider.register(containerInstances);
$stateRegistryProvider.register(containerInstance);
$stateRegistryProvider.register(containerInstanceCreation);
$stateRegistryProvider.register(dashboard);
},

View File

@ -65,8 +65,8 @@
</td>
<td>{{ item.Location }}</td>
<td>
<a ng-if="item.Ports.length > 0" ng-repeat="p in item.Ports" class="image-tag" ng-href="http://{{ item.IPAddress }}:{{ p.port }}" target="_blank">
<i class="fa fa-external-link-alt" aria-hidden="true"></i> {{ item.IPAddress }}:{{ p.port }}
<a ng-if="item.Ports.length > 0" ng-repeat="p in item.Ports" class="image-tag" ng-href="http://{{ item.IPAddress }}:{{ p.host }}" target="_blank">
<i class="fa fa-external-link-alt" aria-hidden="true"></i> {{ item.IPAddress }}:{{ p.host }}
</a>
<span ng-if="item.Ports.length == 0">-</span>
</td>

View File

@ -16,11 +16,20 @@ export function ContainerGroupDefaultModel() {
}
export function ContainerGroupViewModel(data) {
const addressPorts = data.properties.ipAddress.ports;
const container = data.properties.containers.length ? data.properties.containers[0] : {};
const containerPorts = container ? container.properties.ports : [];
this.Id = data.id;
this.Name = data.name;
this.Location = data.location;
this.IPAddress = data.properties.ipAddress.ip;
this.Ports = data.properties.ipAddress.ports;
this.Ports = addressPorts.length ? addressPorts.map((binding, index) => ({ container: containerPorts[index].port, host: binding.port, protocol: binding.protocol })) : [];
this.Image = container.properties.image || '';
this.OSType = data.properties.osType;
this.AllocatePublicIP = data.properties.ipAddress.type === 'Public';
this.CPU = container.properties.resources.requests.cpu;
this.Memory = container.properties.resources.requests.memoryInGB;
}
export function CreateContainerGroupRequest(model) {

View File

@ -34,12 +34,15 @@ angular.module('portainer.azure').factory('ContainerGroup', [
containerGroupName: '@containerGroupName',
},
},
get: {
method: 'GET',
},
}
);
resource.query = base.query;
resource.create = withResourceGroup.create;
resource.get = withResourceGroup.get;
return resource;
},
]);

View File

@ -5,13 +5,14 @@ angular.module('portainer.azure').factory('ResourceGroup', [
function ResourceGroupFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider) {
'use strict';
return $resource(
API_ENDPOINT_ENDPOINTS + '/:endpointId/azure/subscriptions/:subscriptionId/resourcegroups',
API_ENDPOINT_ENDPOINTS + '/:endpointId/azure/subscriptions/:subscriptionId/resourcegroups/:resourceGroupName',
{
endpointId: EndpointProvider.endpointID,
'api-version': '2018-02-01',
},
{
query: { method: 'GET', params: { subscriptionId: '@subscriptionId' } },
get: { method: 'GET' },
}
);
},

View File

@ -5,13 +5,14 @@ angular.module('portainer.azure').factory('Subscription', [
function SubscriptionFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider) {
'use strict';
return $resource(
API_ENDPOINT_ENDPOINTS + '/:endpointId/azure/subscriptions',
API_ENDPOINT_ENDPOINTS + '/:endpointId/azure/subscriptions/:id',
{
endpointId: EndpointProvider.endpointID,
'api-version': '2016-06-01',
},
{
query: { method: 'GET' },
get: { method: 'GET', params: { id: '@id' } },
}
);
},

View File

@ -24,6 +24,12 @@ angular.module('portainer.azure').factory('ContainerGroupService', [
return deferred.promise;
};
service.containerGroup = containerGroup;
async function containerGroup(subscriptionId, resourceGroupName, containerGroupName) {
const containerGroup = await ContainerGroup.get({ subscriptionId, resourceGroupName, containerGroupName }).$promise;
return new ContainerGroupViewModel(containerGroup);
}
service.create = function (model, subscriptionId, resourceGroupName) {
var payload = new CreateContainerGroupRequest(model);
return ContainerGroup.create(

View File

@ -24,6 +24,12 @@ angular.module('portainer.azure').factory('ResourceGroupService', [
return deferred.promise;
};
service.resourceGroup = resourceGroup;
async function resourceGroup(subscriptionId, resourceGroupName) {
const group = await ResourceGroup.get({ subscriptionId, resourceGroupName }).$promise;
return new ResourceGroupViewModel(group);
}
return service;
},
]);

View File

@ -24,6 +24,12 @@ angular.module('portainer.azure').factory('SubscriptionService', [
return deferred.promise;
};
service.subscription = subscription;
async function subscription(id) {
const subscription = await Subscription.get({ id }).$promise;
return new SubscriptionViewModel(subscription);
}
return service;
},
]);

View File

@ -0,0 +1,134 @@
<rd-header>
<rd-header-title title-text="Create container instance"></rd-header-title>
<rd-header-content> <a ui-sref="azure.containerinstances">Container instances</a> &gt; {{ $ctrl.container.Name }} </rd-header-content>
</rd-header>
<div class="row" ng-if="!$ctrl.state.loading">
<div class="col-sm-12">
<rd-widget>
<rd-widget-body>
<div class="form-horizontal" autocomplete="off">
<div class="col-sm-12 form-section-title">
Azure settings
</div>
<!-- subscription-input -->
<div class="form-group">
<label for="azure_subscription" class="col-sm-2 control-label text-left">Subscription</label>
<div class="col-sm-10">
<input type="text" class="form-control" ng-model="$ctrl.subscription.Name" disabled />
</div>
</div>
<!-- !subscription-input -->
<!-- resourcegroup-input -->
<div class="form-group">
<label for="azure_resourcegroup" class="col-sm-2 control-label text-left">Resource group</label>
<div class="col-sm-10">
<input type="text" class="form-control" ng-model="$ctrl.resourceGroup.Name" disabled />
</div>
</div>
<!-- !resourcegroup-input -->
<!-- location-input -->
<div class="form-group">
<label for="azure_location" class="col-sm-2 control-label text-left">Location</label>
<div class="col-sm-10">
<input type="text" class="form-control" ng-model="$ctrl.container.Location" disabled />
</div>
</div>
<!-- !location-input -->
<div class="col-sm-12 form-section-title">
Container configuration
</div>
<!-- name-input -->
<div class="form-group">
<label for="container_name" class="col-sm-2 control-label text-left">Name</label>
<div class="col-sm-10">
<input type="text" class="form-control" ng-model="$ctrl.container.Name" disabled />
</div>
</div>
<!-- !name-input -->
<!-- image-input -->
<div class="form-group">
<label for="image_name" class="col-sm-2 control-label text-left">Image</label>
<div class="col-sm-10">
<input type="text" class="form-control" ng-model="$ctrl.container.Image" disabled />
</div>
</div>
<!-- !image-input -->
<!-- os-input -->
<div class="form-group">
<label for="container_os" class="col-sm-2 control-label text-left">OS</label>
<div class="col-sm-10">
<input type="text" class="form-control" ng-model="$ctrl.container.OSType" disabled />
</div>
</div>
<!-- !os-input -->
<!-- port-mapping -->
<div class="form-group">
<div class="col-sm-12">
<label class="control-label text-left">Port mapping</label>
</div>
<!-- port-mapping-input-list -->
<div class="col-sm-12 form-inline" style="margin-top: 10px;">
<div ng-repeat="binding in $ctrl.container.Ports" style="margin-top: 2px;">
<!-- host-port -->
<div class="input-group col-sm-4 input-group-sm">
<span class="input-group-addon">host</span>
<input type="text" class="form-control" ng-model="binding.host" placeholder="e.g. 80" disabled />
</div>
<!-- !host-port -->
<span style="margin: 0 10px 0 10px;">
<i class="fa fa-long-arrow-alt-right" aria-hidden="true"></i>
</span>
<!-- container-port -->
<div class="input-group col-sm-4 input-group-sm">
<span class="input-group-addon">container</span>
<input type="text" class="form-control" ng-model="binding.container" placeholder="e.g. 80" disabled />
</div>
<!-- !container-port -->
<!-- protocol-actions -->
<div class="input-group col-sm-3 input-group-sm">
<div class="btn-group btn-group-sm">
<label class="btn btn-primary" ng-model="binding.protocol" uib-btn-radio="'TCP'" disabled>TCP</label>
<label class="btn btn-primary" ng-model="binding.protocol" uib-btn-radio="'UDP'" disabled>UDP</label>
</div>
</div>
<!-- !protocol-actions -->
</div>
</div>
<!-- !port-mapping-input-list -->
</div>
<!-- !port-mapping -->
<!-- public-ip -->
<div class="form-group" ng-if="$ctrl.container.AllocatePublicIP">
<label for="public_ip" class="col-sm-2 control-label text-left">
Public IP
</label>
<div class="col-sm-10">
<input type="text" class="form-control" ng-model="$ctrl.container.IPAddress" disabled />
</div>
</div>
<!-- public-ip -->
<div class="col-sm-12 form-section-title">
Container resources
</div>
<!-- cpu-input -->
<div class="form-group">
<label for="container_cpu" class="col-sm-2 control-label text-left">CPU</label>
<div class="col-sm-10">
<input type="text" class="form-control" ng-model="$ctrl.container.CPU" disabled />
</div>
</div>
<!-- !cpu-input -->
<!-- memory-input -->
<div class="form-group">
<label for="container_memory" class="col-sm-2 control-label text-left">Memory</label>
<div class="col-sm-10">
<input type="text" class="form-control" ng-model="$ctrl.container.Memory" disabled />
</div>
</div>
<!-- !memory-input -->
</div>
</rd-widget-body>
</rd-widget>
</div>
</div>

View File

@ -0,0 +1,36 @@
class ContainerInstanceDetailsController {
/* @ngInject */
constructor($state, AzureService, ContainerGroupService, Notifications, ResourceGroupService, SubscriptionService) {
Object.assign(this, { $state, AzureService, ContainerGroupService, Notifications, ResourceGroupService, SubscriptionService });
this.state = {
loading: false,
};
this.container = null;
this.subscription = null;
this.resourceGroup = null;
}
async $onInit() {
this.state.loading = true;
const { id } = this.$state.params;
const { subscriptionId, resourceGroupId, containerGroupId } = parseId(id);
try {
this.subscription = await this.SubscriptionService.subscription(subscriptionId);
this.container = await this.ContainerGroupService.containerGroup(subscriptionId, resourceGroupId, containerGroupId);
this.resourceGroup = await this.ResourceGroupService.resourceGroup(subscriptionId, resourceGroupId);
} catch (err) {
this.Notifications.error('Failure', err, 'Unable to retrive container instance details');
}
this.state.loading = false;
}
}
function parseId(id) {
const [, subscriptionId, resourceGroupId, , containerGroupId] = id.match(/^\/subscriptions\/(.+)\/resourceGroups\/(.+)\/providers\/(.+)\/containerGroups\/(.+)$/);
return { subscriptionId, resourceGroupId, containerGroupId };
}
export default ContainerInstanceDetailsController;

View File

@ -0,0 +1,6 @@
import ContainerInstanceDetailsController from './containerInstanceDetailsController.js';
angular.module('portainer.azure').component('containerInstanceDetails', {
templateUrl: './containerInstanceDetails.html',
controller: ContainerInstanceDetailsController,
});