Merge pull request #4634 from ryanpetrello/licensing-rhsm-changes-380

reimplement licensing to work with RHSM and entitlement uploads
This commit is contained in:
Ryan Petrello
2020-10-30 13:03:08 -04:00
committed by GitHub
34 changed files with 787 additions and 275 deletions

View File

@@ -54,20 +54,20 @@ export default {
});
}],
resolve: {
rhCreds: ['Rest', 'GetBasePath', function(Rest, GetBasePath) {
subscriptionCreds: ['Rest', 'GetBasePath', function(Rest, GetBasePath) {
Rest.setUrl(`${GetBasePath('settings')}system/`);
return Rest.get()
.then(({data}) => {
const rhCreds = {};
if (data.REDHAT_USERNAME && data.REDHAT_USERNAME !== "") {
rhCreds.REDHAT_USERNAME = data.REDHAT_USERNAME;
const subscriptionCreds = {};
if (data.SUBSCRIPTIONS_USERNAME && data.SUBSCRIPTIONS_USERNAME !== "") {
subscriptionCreds.SUBSCRIPTIONS_USERNAME = data.SUBSCRIPTIONS_USERNAME;
}
if (data.REDHAT_PASSWORD && data.REDHAT_PASSWORD !== "") {
rhCreds.REDHAT_PASSWORD = data.REDHAT_PASSWORD;
if (data.SUBSCRIPTIONS_PASSWORD && data.SUBSCRIPTIONS_PASSWORD !== "") {
subscriptionCreds.SUBSCRIPTIONS_PASSWORD = data.SUBSCRIPTIONS_PASSWORD;
}
return rhCreds;
return subscriptionCreds;
}).catch(() => {
return {};
});

View File

@@ -15,14 +15,26 @@ export default
return config.license_info;
},
post: function(payload, eula){
var defaultUrl = GetBasePath('config');
post: function(payload, eula, attach){
var defaultUrl = GetBasePath('config') + (attach ? 'attach/' : '');
Rest.setUrl(defaultUrl);
var data = payload;
data.eula_accepted = eula;
if (!attach) {
data.eula_accepted = eula;
}
return Rest.post(JSON.stringify(data))
.then((response) =>{
if (attach) {
var configPayload = {};
configPayload.eula_accepted = eula;
Rest.setUrl(GetBasePath('config'));
return Rest.post(configPayload)
.then((configResponse) => {
return configResponse.data;
});
}
return response.data;
})
.catch(({data}) => {

View File

@@ -8,9 +8,9 @@ import {N_} from "../i18n";
export default
['Wait', '$state', '$scope', '$rootScope', 'ProcessErrors', 'CheckLicense', 'moment', '$timeout', 'Rest', 'LicenseStrings',
'$window', 'ConfigService', 'pendoService', 'insightsEnablementService', 'i18n', 'config', 'rhCreds', 'GetBasePath',
'$window', 'ConfigService', 'pendoService', 'insightsEnablementService', 'i18n', 'config', 'subscriptionCreds', 'GetBasePath',
function(Wait, $state, $scope, $rootScope, ProcessErrors, CheckLicense, moment, $timeout, Rest, LicenseStrings,
$window, ConfigService, pendoService, insightsEnablementService, i18n, config, rhCreds, GetBasePath) {
$window, ConfigService, pendoService, insightsEnablementService, i18n, config, subscriptionCreds, GetBasePath) {
$scope.strings = LicenseStrings;
@@ -35,7 +35,7 @@ export default
const reset = function() {
$scope.newLicense.eula = undefined;
$scope.rhCreds = {};
$scope.subscriptionCreds = {};
$scope.selectedLicense = {};
};
@@ -44,9 +44,9 @@ export default
$scope.fileName = N_("No file selected.");
if ($rootScope.licenseMissing) {
$scope.title = $rootScope.BRAND_NAME + i18n._(" License");
$scope.title = $rootScope.BRAND_NAME + i18n._(" Subscription");
} else {
$scope.title = i18n._("License Management");
$scope.title = i18n._("Subscription Management");
}
$scope.license = config;
@@ -62,30 +62,30 @@ export default
insights: true
};
$scope.rhCreds = {};
$scope.subscriptionCreds = {};
if (rhCreds.REDHAT_USERNAME && rhCreds.REDHAT_USERNAME !== "") {
$scope.rhCreds.username = rhCreds.REDHAT_USERNAME;
if (subscriptionCreds.SUBSCRIPTIONS_USERNAME && subscriptionCreds.SUBSCRIPTIONS_USERNAME !== "") {
$scope.subscriptionCreds.username = subscriptionCreds.SUBSCRIPTIONS_USERNAME;
}
if (rhCreds.REDHAT_PASSWORD && rhCreds.REDHAT_PASSWORD !== "") {
$scope.rhCreds.password = rhCreds.REDHAT_PASSWORD;
if (subscriptionCreds.SUBSCRIPTIONS_PASSWORD && subscriptionCreds.SUBSCRIPTIONS_PASSWORD !== "") {
$scope.subscriptionCreds.password = subscriptionCreds.SUBSCRIPTIONS_PASSWORD;
$scope.showPlaceholderPassword = true;
}
};
const updateRHCreds = (config) => {
const updateSubscriptionCreds = (config) => {
Rest.setUrl(`${GetBasePath('settings')}system/`);
Rest.get()
.then(({data}) => {
initVars(config);
if (data.REDHAT_USERNAME && data.REDHAT_USERNAME !== "") {
$scope.rhCreds.username = data.REDHAT_USERNAME;
if (data.SUBSCRIPTIONS_USERNAME && data.SUBSCRIPTIONS_USERNAME !== "") {
$scope.subscriptionCreds.username = data.SUBSCRIPTIONS_USERNAME;
}
if (data.REDHAT_PASSWORD && data.REDHAT_PASSWORD !== "") {
$scope.rhCreds.password = data.REDHAT_PASSWORD;
if (data.SUBSCRIPTIONS_PASSWORD && data.SUBSCRIPTIONS_PASSWORD !== "") {
$scope.subscriptionCreds.password = data.SUBSCRIPTIONS_PASSWORD;
$scope.showPlaceholderPassword = true;
}
}).catch(() => {
@@ -100,28 +100,23 @@ export default
$scope.fileName = event.target.files[0].name;
// Grab the key from the raw license file
const raw = new FileReader();
// readAsFoo runs async
raw.onload = function() {
try {
$scope.newLicense.file = JSON.parse(raw.result);
} catch(err) {
ProcessErrors($rootScope, null, null, null,
{msg: i18n._('Invalid file format. Please upload valid JSON.')});
}
$scope.newLicense.manifest = btoa(raw.result);
};
try {
raw.readAsText(event.target.files[0]);
raw.readAsBinaryString(event.target.files[0]);
} catch(err) {
ProcessErrors($rootScope, null, null, null,
{msg: i18n._('Invalid file format. Please upload valid JSON.')});
{msg: i18n._('Invalid file format. Please upload a valid Red Hat Subscription Manifest.')});
}
};
// HTML5 spec doesn't provide a way to customize file input css
// So we hide the default input, show our own, and simulate clicks to the hidden input
$scope.fakeClick = function() {
if($scope.user_is_superuser && (!$scope.rhCreds.username || $scope.rhCreds.username === '') && (!$scope.rhCreds.password || $scope.rhCreds.password === '')) {
if($scope.user_is_superuser && (!$scope.subscriptionCreds.username || $scope.subscriptionCreds.username === '') && (!$scope.subscriptionCreds.password || $scope.subscriptionCreds.password === '')) {
$('#License-file').click();
}
};
@@ -131,9 +126,9 @@ export default
};
$scope.replacePassword = () => {
if ($scope.user_is_superuser && !$scope.newLicense.file) {
if ($scope.user_is_superuser && !$scope.newLicense.manifest) {
$scope.showPlaceholderPassword = false;
$scope.rhCreds.password = "";
$scope.subscriptionCreds.password = "";
$timeout(() => {
$('.tooltip').remove();
$('#rh-password').focus();
@@ -142,9 +137,9 @@ export default
};
$scope.lookupLicenses = () => {
if ($scope.rhCreds.username && $scope.rhCreds.password) {
if ($scope.subscriptionCreds.username && $scope.subscriptionCreds.password) {
Wait('start');
ConfigService.getSubscriptions($scope.rhCreds.username, $scope.rhCreds.password)
ConfigService.getSubscriptions($scope.subscriptionCreds.username, $scope.subscriptionCreds.password)
.then(({data}) => {
Wait('stop');
if (data && data.length > 0) {
@@ -172,29 +167,30 @@ export default
$scope.confirmLicenseSelection = () => {
$scope.showLicenseModal = false;
$scope.selectedLicense.fullLicense = $scope.rhLicenses.find((license) => {
return license.license_key === $scope.selectedLicense.modalKey;
return license.pool_id === $scope.selectedLicense.modalPoolId;
});
$scope.selectedLicense.modalKey = undefined;
$scope.selectedLicense.modalPoolId = undefined;
};
$scope.cancelLicenseLookup = () => {
$scope.showLicenseModal = false;
$scope.selectedLicense.modalKey = undefined;
$scope.selectedLicense.modalPoolId = undefined;
};
$scope.submit = function() {
Wait('start');
let payload = {};
if ($scope.newLicense.file) {
payload = $scope.newLicense.file;
let attach = false;
if ($scope.newLicense.manifest) {
payload.manifest = $scope.newLicense.manifest;
} else if ($scope.selectedLicense.fullLicense) {
payload = $scope.selectedLicense.fullLicense;
payload.pool_id = $scope.selectedLicense.fullLicense.pool_id;
attach = true;
}
CheckLicense.post(payload, $scope.newLicense.eula)
.then((licenseInfo) => {
CheckLicense.post(payload, $scope.newLicense.eula, attach)
.finally((licenseInfo) => {
reset();
ConfigService.delete();
ConfigService.getConfig(licenseInfo)
.then(function(config) {
@@ -217,7 +213,7 @@ export default
licenseMissing: false
});
} else {
updateRHCreds(config);
updateSubscriptionCreds(config);
$scope.success = true;
$rootScope.licenseMissing = false;
// for animation purposes

View File

@@ -5,10 +5,10 @@
<div class="List-titleText" translate>Details</div>
<div class="License-fields">
<div class="License-field">
<div class="License-field--label" translate>License</div>
<div class="License-field--label" translate>Subscription</div>
<div class="License-field--content">
<span class="License-greenText" ng-show='compliant'><i class="fa fa-circle License-greenText"></i><translate>Valid License</translate></span>
<span class="License-redText" ng-show='compliant !== undefined && !compliant'><i class="fa fa-circle License-redText"></i><translate>Invalid License</translate></span>
<span class="License-greenText" ng-show='compliant'><i class="fa fa-circle License-greenText"></i><translate>Compliant</translate></span>
<span class="License-redText" ng-show='compliant !== undefined && !compliant'><i class="fa fa-circle License-redText"></i><translate>Out of Compliance</translate></span>
</div>
</div>
<div class="License-field">
@@ -18,7 +18,7 @@
</div>
</div>
<div class="License-field">
<div class="License-field--label" translate>License Type</div>
<div class="License-field--label" translate>Subscription Type</div>
<div class="License-field--content">
{{license.license_info.license_type}}
</div>
@@ -29,12 +29,6 @@
{{license.license_info.subscription_name}}
</div>
</div>
<div class="License-field">
<div class="License-field--label" translate>License Key</div>
<div class="License-field--content License-field--key">
{{license.license_info.license_key}}
</div>
</div>
<div class="License-field">
<div class="License-field--label" translate>Expires On</div>
<div class="License-field--content">
@@ -64,53 +58,66 @@
{{license.license_info.current_instances}}
</div>
</div>
<div class="License-field License-greenText" ng-show='license.license_info.available_instances < 9999999'>
<div class="License-field License-greenText" ng-show='license.license_info.available_instances < 9999999 && compliant'>
<div class="License-field--label" translate>Hosts Remaining</div>
<div class="License-field--content">
{{license.license_info.free_instances}}
</div>
</div>
<div class="License-field License-redText" ng-show='license.license_info.free_instances < 1 && !compliant'>
<div class="License-field--label" translate>Hosts Remaining</div>
<div class="License-field--content">
{{license.license_info.free_instances}}
</div>
</div>
</div>
<div class="License-upgradeText" translate>If you are ready to upgrade, please contact us by clicking the button below</div>
<a href="https://www.ansible.com/renew" target="_blank"><button class="btn btn-primary" translate>Upgrade</button></a>
<div class="License-upgradeText" translate>If you are ready to upgrade or renew, please contact us by clicking the button below.</div>
<a href="https://www.redhat.com/contact" target="_blank"><button class="btn btn-primary" translate>Contact Us</button></a>
</div>
</div>
<div class="License-management" ng-class="{'License-management--missingLicense' : licenseMissing}">
<div class="card at-Panel">
<div class="List-titleText">{{title}}</div>
<div class="License-body">
<div class="License-helperText License-introText" ng-if="licenseMissing" translate>Welcome to Ansible Tower! Please complete the steps below to acquire a license.</div>
<div class="License-helperText License-introText" ng-if="licenseMissing" translate>Welcome to Red Hat Ansible Automation Platform! Please complete the steps below to activate your subscription.</div>
<div class="AddPermissions-directions" ng-if="licenseMissing">
<span class="AddPermissions-directionNumber">
1
</span>
<span class="License-helperText">
<translate>If you do not have a subscription, you can visit Red Hat to obtain a trial subscription.</translate>
</span>
</div>
<button class="License-downloadLicenseButton btn btn-primary" ng-if="licenseMissing" ng-click="downloadLicense()">
<translate>Request Subscription</translate>
</button>
<div class="License-file-container">
<div class="AddPermissions-directions" ng-if="licenseMissing">
<span class="AddPermissions-directionNumber" ng-if="licenseMissing">
2
</span>
<span class="License-helperText">
<translate>Select your Ansible Automation Platform subscription to use.</translate>
</span>
</div>
</div>
<div class="input-group License-file--container">
<div class="License-file--left">
<div class="d-block w-100">
<div class="AddPermissions-directions" ng-if="licenseMissing">
<span class="AddPermissions-directionNumber">
1
</span>
<span class="License-helperText">
<translate>Please click the button below to visit Ansible's website to get a Tower license key.</translate>
</span>
</div>
<button class="License-downloadLicenseButton btn btn-primary" ng-if="licenseMissing" ng-click="downloadLicense()">
<translate>Request License</translate>
</button>
<div class="AddPermissions-directions">
<span class="AddPermissions-directionNumber" ng-if="licenseMissing">
2
</span>
<span class="License-helperText">
<translate>Choose your license file, agree to the End User License Agreement, and click submit.</translate>
<translate>Upload a Red Hat Subscription Manifest containing your subscription. To generate your subscription manifest, go to <a href="https://access.redhat.com/management/subscription_allocations" target="_blank">subscription allocations</a> on the Red Hat Customer Portal.</translate>
</span>
</div>
<div class="License-subTitleText">
<span class="Form-requiredAsterisk">*</span>
<translate>License</translate>
<translate>Red Hat Subscription Manifest</translate>
<a aria-label="{{'Show help text' | translate}}" id="subscription-manifest-popover" href="" aw-pop-over="A subscription manifest is an export of a Red Hat Subscription. To generate a subscription manifest, go to <a href=https://access.redhat.com/management/subscription_allocations target=_blank>access.redhat.com</a>.<br>For more information, see the <a href=https://docs.ansible.com/ansible-tower/latest/html/userguide/import_license.html>User Guide</a>.</translate>" data-placement="top" data-container="body" over-title="Subscription Manifest" class="help-link">
<i class="fa fa-question-circle"></i>
</a>
</div>
<div class="License-helperText License-licenseStepHelp" translate>Upload a license file</div>
<div class="License-filePicker">
<span class="btn btn-primary" ng-click="fakeClick()" ng-disabled="!user_is_superuser || (rhCreds.username && rhCreds.username.length > 0) || (rhCreds.password && rhCreds.password.length > 0)" translate>Browse</span>
<span class="btn btn-primary" ng-click="fakeClick()" ng-disabled="!user_is_superuser || (subscriptionCreds.username && subscriptionCreds.username.length > 0) || (subscriptionCreds.password && subscriptionCreds.password.length > 0)" translate>Browse</span>
<span class="License-fileName" ng-class="{'License-helperText' : fileName == 'No file selected.'}">{{fileName|translate}}</span>
<input id="License-file" class="form-control" type="file" file-on-change="getKey"/>
</div>
@@ -125,12 +132,12 @@
<div class="d-block w-100">
<div class="AddPermissions-directions">
<span class="License-helperText">
<translate>Provide your Red Hat customer credentials and you can choose from a list of your available licenses. The credentials you use will be stored for future use in retrieving renewal or expanded licenses. You can update or remove them in SETTINGS &gt; SYSTEM.</translate>
<translate>Provide your Red Hat or Red Hat Satellite credentials below and you can choose from a list of your available subscriptions. The credentials you use will be stored for future use in retrieving renewal or expanded subscriptions.</translate>
</span>
</div>
<div class="License-rhCredField">
<label class="License-label d-block" translate>USERNAME</label>
<input class="form-control Form-textInput" type="text" ng-model="rhCreds.username" ng-disabled="!user_is_superuser || newLicense.file" />
<input class="form-control Form-textInput" type="text" ng-model="subscriptionCreds.username" ng-disabled="!user_is_superuser || newLicense.file" />
</div>
<div class="License-rhCredField">
<label class="License-label d-block" translate>PASSWORD</label>
@@ -143,11 +150,11 @@
</span>
</div>
<div class="input-group" ng-if="!showPlaceholderPassword">
<input id="rh-password" class="form-control Form-textInput" type="password" ng-model="rhCreds.password" ng-disabled="!user_is_superuser || newLicense.file" />
<input id="rh-password" class="form-control Form-textInput" type="password" ng-model="subscriptionCreds.password" ng-disabled="!user_is_superuser || newLicense.file" />
</div>
</div>
<div class="License-getLicensesButton">
<span ng-click="lookupLicenses()" class="btn btn-primary" ng-disabled="!rhCreds.username || !rhCreds.password" translate>GET LICENSES</button>
<span ng-click="lookupLicenses()" class="btn btn-primary" ng-disabled="!subscriptionCreds.username || !subscriptionCreds.password" translate>GET SUBSCRIPTIONS</button>
</div>
<div ng-if="selectedLicense.fullLicense">
<div class="at-RowItem-label" translate>
@@ -158,6 +165,14 @@
</div>
</div>
</div>
<div class="License-helperText">
<span class="AddPermissions-directionNumber" ng-if="licenseMissing">
3
</span>
<span class="License-helperText">
<translate>Agree to the End User License Agreement, and click submit.</translate>
</span>
</div>
<div class="License-subTitleText">
<span class="Form-requiredAsterisk">*</span>
<translate>End User License Agreement</translate>
@@ -200,7 +215,7 @@
<span ng-show="success == true" class="License-greenText License-submit--success pull-right" translate>Save successful!</span>
</div>
<div>
<button ng-click="submit()" class="btn btn-success pull-right" ng-disabled="(!newLicense.file && !selectedLicense.fullLicense) || (newLicense.file && newLicense.file.license_key == null) || newLicense.eula == null || !user_is_superuser" translate>Submit</button>
<button ng-click="submit()" class="btn btn-success pull-right" ng-disabled="(!newLicense.manifest && !selectedLicense.fullLicense) || newLicense.eula == null || !user_is_superuser" translate>Submit</button>
</div>
</div>
</div>
@@ -223,12 +238,12 @@
<div class="Modal-body ng-binding">
<div class="License-modalBody">
<form>
<div class="License-modalRow" ng-repeat="license in rhLicenses track by license.license_key">
<div class="License-modalRow" ng-repeat="license in rhLicenses track by license.pool_id">
<div class="License-modalRowRadio">
<input type="radio" id="license-{{license.license_key}}" ng-model="selectedLicense.modalKey" value="{{license.license_key}}" />
<input type="radio" id="license-{{license.pool_id}}" ng-model="selectedLicense.modalPoolId" value="{{license.pool_id}}"/>
</div>
<div class="License-modalRowDetails">
<label for="license-{{license.license_key}}" class="License-modalRowDetailsLabel">
<label for="license-{{license.pool_id}}" class="License-modalRowDetailsLabel">
<div class="License-modalRowDetailsRow">
<div class="License-trialTag" ng-if="license.trial" translate>
Trial
@@ -260,7 +275,7 @@
<button
ng-click="confirmLicenseSelection()"
class="btn Modal-footerButton btn-success"
ng-disabled="!selectedLicense.modalKey"
ng-disabled="!selectedLicense.modalPoolId"
translate
>
SELECT

View File

@@ -15,7 +15,7 @@ export default {
controller: 'licenseController',
data: {},
ncyBreadcrumb: {
label: N_('LICENSE')
label: N_('SUBSCRIPTION')
},
onEnter: ['$state', 'ConfigService', (state, configService) => {
return configService.getConfig()
@@ -43,20 +43,20 @@ export default {
});
}
],
rhCreds: ['Rest', 'GetBasePath', function(Rest, GetBasePath) {
subscriptionCreds: ['Rest', 'GetBasePath', function(Rest, GetBasePath) {
Rest.setUrl(`${GetBasePath('settings')}system/`);
return Rest.get()
.then(({data}) => {
const rhCreds = {};
if (data.REDHAT_USERNAME && data.REDHAT_USERNAME !== "") {
rhCreds.REDHAT_USERNAME = data.REDHAT_USERNAME;
const subscriptionCreds = {};
if (data.SUBSCRIPTIONS_USERNAME && data.SUBSCRIPTIONS_USERNAME !== "") {
subscriptionCreds.SUBSCRIPTIONS_USERNAME = data.SUBSCRIPTIONS_USERNAME;
}
if (data.REDHAT_PASSWORD && data.REDHAT_PASSWORD !== "") {
rhCreds.REDHAT_PASSWORD = data.REDHAT_PASSWORD;
if (data.SUBSCRIPTIONS_PASSWORD && data.SUBSCRIPTIONS_PASSWORD !== "") {
subscriptionCreds.SUBSCRIPTIONS_PASSWORD = data.SUBSCRIPTIONS_PASSWORD;
}
return rhCreds;
return subscriptionCreds;
}).catch(() => {
return {};
});

View File

@@ -62,7 +62,7 @@ export default
getSubscriptions: function(username, password) {
Rest.setUrl(`${GetBasePath('config')}subscriptions`);
return Rest.post({ rh_username: username, rh_password: password} );
return Rest.post({ subscriptions_username: username, subscriptions_password: password} );
}
};
}

View File

@@ -7,7 +7,7 @@ describe('Controller: LicenseController', () => {
ConfigService,
ProcessErrors,
config,
rhCreds;
subscriptionCreds;
beforeEach(angular.mock.module('awApp'));
beforeEach(angular.mock.module('license', ($provide) => {
@@ -23,7 +23,7 @@ describe('Controller: LicenseController', () => {
version: '3.1.0-devel'
};
rhCreds = {
subscriptionCreds = {
password: '$encrypted$',
username: 'foo',
}
@@ -33,21 +33,21 @@ describe('Controller: LicenseController', () => {
$provide.value('ConfigService', ConfigService);
$provide.value('ProcessErrors', ProcessErrors);
$provide.value('config', config);
$provide.value('rhCreds', rhCreds);
$provide.value('subscriptionCreds', subscriptionCreds);
}));
beforeEach(angular.mock.inject( ($rootScope, $controller, _ConfigService_, _ProcessErrors_, _config_, _rhCreds_) => {
beforeEach(angular.mock.inject( ($rootScope, $controller, _ConfigService_, _ProcessErrors_, _config_, _subscriptionCreds_) => {
scope = $rootScope.$new();
ConfigService = _ConfigService_;
ProcessErrors = _ProcessErrors_;
config = _config_;
rhCreds = _rhCreds_;
subscriptionCreds = _subscriptionCreds_;
LicenseController = $controller('licenseController', {
$scope: scope,
ConfigService: ConfigService,
ProcessErrors: ProcessErrors,
config: config,
rhCreds: rhCreds
subscriptionCreds: subscriptionCreds
});
}));