initial import for Open Source 🎉
This commit is contained in:
parent
1898c361f3
commit
9c0dd3b722
2048 changed files with 218743 additions and 0 deletions
|
@ -0,0 +1,71 @@
|
|||
<div>
|
||||
<div class="co-dialog modal fade initial-setup-modal in" id="setupModal" style="display: block;">
|
||||
<div class="modal-backdrop fade in" style="height: 1000px;"></div>
|
||||
<div class="modal-dialog fade in">
|
||||
<div class="modal-content kube-deploy-modal">
|
||||
<!-- Header -->
|
||||
<div class="modal-header">
|
||||
<span class="cor-step-bar">
|
||||
<span class="cor-step active" title="Configure Database" text="1"></span>
|
||||
<span class="cor-step active" title="Setup Database" icon="database"></span>
|
||||
<span class="cor-step active" title="Create Superuser" text="2"></span>
|
||||
<span class="cor-step active" title="Configure Registry" text="3"></span>
|
||||
<span class="cor-step active" title="Validate Configuration" text="4"></span>
|
||||
<span class="cor-step active" title="Setup Complete" icon="download"></span>
|
||||
<span class="cor-step active" title="Deploy Complete" icon="paper-plane"></span>
|
||||
</span>
|
||||
<h4 class="modal-title"><span>Deploy configuration</span></h4>
|
||||
</div>
|
||||
<!-- Body -->
|
||||
<div class="modal-body">
|
||||
<div class="cor-loader" ng-if="$ctrl.state === 'loadingDeployments'"></div>
|
||||
<div class="kube-deploy-modal__body" ng-if="$ctrl.deploymentsStatus.length > 0">
|
||||
<span class="kube-deploy-modal__list-header">The following deployments will be affected:</span>
|
||||
<ul class="kube-deploy-modal__list">
|
||||
<li class="kube-deploy-modal__list-item" ng-repeat="deployment in $ctrl.deploymentsStatus">
|
||||
<i class="fa ci-k8s-logo"></i>
|
||||
<code>{{deployment.name}}</code> with {{deployment.numPods}} <b> {{ deployment.numPods === 1 ? ' pod' : ' pods' }}</b>
|
||||
</li>
|
||||
</ul>
|
||||
<button ng-if="$ctrl.state === 'readyToDeploy'" class="btn btn-primary btn-lg" ng-click="$ctrl.deployConfiguration()">
|
||||
<i class="far fa-paper-plane" style="margin-right: 10px;"></i>
|
||||
Populate configuration to deployments
|
||||
</button>
|
||||
<div ng-if="$ctrl.state === 'deployingConfiguration'">
|
||||
<div class="cor-loader"></div>
|
||||
Deploying configuration...
|
||||
</div>
|
||||
<div ng-if="$ctrl.state === 'cyclingDeployments'">
|
||||
<div class="cor-loader"></div>
|
||||
<span class="kube-deploy-modal__list-header">Cycling deployments...</span>
|
||||
<ul class="kube-deploy-modal__list">
|
||||
<li class="kube-deploy-modal__list-item" ng-repeat="deployment in $ctrl.deploymentsStatus">
|
||||
<i class="fa ci-k8s-logo"></i>
|
||||
<code>{{deployment.name}}</code>: {{deployment.message || 'Waiting for deployment information...'}}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="$ctrl.state === 'deployed'" class="modal-footer co-alert co-alert-success">
|
||||
Configuration successfully rolled out and deployed!
|
||||
<br>Note: The web interface of the Quay app may take a few minutes to come up.
|
||||
</div>
|
||||
<div ng-if="$ctrl.state === 'error'" class="modal-footer alert alert-danger">
|
||||
{{ $ctrl.errorMessage }}
|
||||
<div ng-if="$ctrl.rollingBackStatus !== 'none'" class="rollback">
|
||||
<button ng-if="$ctrl.rollingBackStatus === 'offer'" class="btn btn-default" ng-click="$ctrl.rollbackDeployments()">
|
||||
<i class="fas fa-history" style="margin-right: 10px;"></i>
|
||||
Rollback deployments
|
||||
</button>
|
||||
<div ng-if="$ctrl.rollingBackStatus === 'rolling'" class="cor-loader-inline"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="$ctrl.state === 'rolledBackWarning'" class="modal-footer co-alert co-alert-warning">
|
||||
Successfully rolled back changes. Please try deploying again with your configuration later.
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,153 @@
|
|||
import { Input, Component, Inject, OnDestroy } from 'ng-metadata/core';
|
||||
import { AngularPollChannel, PollHandle } from "../../services/services.types";
|
||||
const templateUrl = require('./kube-deploy-modal.component.html');
|
||||
const styleUrl = require('./kube-deploy-modal.css');
|
||||
|
||||
// The response from the API about deployment rollout status
|
||||
type DeploymentRollout = {
|
||||
status: 'available' | 'progressing' | 'failed',
|
||||
message: string
|
||||
};
|
||||
|
||||
type DeploymentStatus = {
|
||||
name: string,
|
||||
numPods: number,
|
||||
message?: string,
|
||||
pollHandler?: PollHandle,
|
||||
}
|
||||
|
||||
const DEPLOYMENT_POLL_SLEEPTIME = 5000; /* 5 seconds */
|
||||
|
||||
@Component({
|
||||
selector: 'kube-deploy-modal',
|
||||
templateUrl,
|
||||
styleUrls: [ styleUrl ],
|
||||
})
|
||||
export class KubeDeployModalComponent implements OnDestroy {
|
||||
@Input('<') public loadedConfig;
|
||||
private state
|
||||
: 'loadingDeployments'
|
||||
| 'readyToDeploy'
|
||||
| 'deployingConfiguration'
|
||||
| 'cyclingDeployments'
|
||||
| 'deployed'
|
||||
| 'error'
|
||||
| 'rolledBackWarning' = 'loadingDeployments';
|
||||
private errorMessage: string;
|
||||
private deploymentsStatus: DeploymentStatus[] = [];
|
||||
private deploymentsCycled: number = 0;
|
||||
private onDestroyListeners: Function[] = [];
|
||||
private rollingBackStatus
|
||||
: 'none'
|
||||
| 'offer'
|
||||
| 'rolling' = 'none';
|
||||
|
||||
constructor(@Inject('ApiService') private ApiService, @Inject('AngularPollChannel') private AngularPollChannel: AngularPollChannel) {
|
||||
ApiService.scGetNumDeployments().then(resp => {
|
||||
this.deploymentsStatus = resp.items.map(dep => ({ name: dep.metadata.name, numPods: dep.spec.replicas }));
|
||||
this.state = 'readyToDeploy';
|
||||
}).catch(err => {
|
||||
this.state = 'error';
|
||||
this.errorMessage = `There are no Quay deployments active in this namespace. \
|
||||
Please check that you are running this \
|
||||
tool in the same namespace as the Red Hat Quay application\
|
||||
Associated error message: ${err.toString()}`;
|
||||
})
|
||||
}
|
||||
|
||||
// Call all listeners of the onDestroy
|
||||
ngOnDestroy(): any {
|
||||
this.onDestroyListeners.forEach(fn => {
|
||||
fn()
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
deployConfiguration(): void {
|
||||
this.ApiService.scDeployConfiguration().then(() => {
|
||||
this.state = 'deployingConfiguration';
|
||||
const deploymentNames: string[] = this.deploymentsStatus.map(dep => dep.name);
|
||||
|
||||
this.ApiService.scCycleQEDeployments({ deploymentNames }).then(() => {
|
||||
this.state = 'cyclingDeployments';
|
||||
this.watchDeployments();
|
||||
}).catch(err => {
|
||||
this.state = 'error';
|
||||
this.errorMessage = `Could not cycle the deployments with the new configuration. Error: ${err.toString()}`;
|
||||
})
|
||||
}).catch(err => {
|
||||
this.state = 'error';
|
||||
this.errorMessage = `Could not deploy the configuration. Error: ${err.toString()}`;
|
||||
})
|
||||
}
|
||||
|
||||
watchDeployments(): void {
|
||||
this.deploymentsStatus.forEach(deployment => {
|
||||
const pollChannel = this.AngularPollChannel.create({
|
||||
// Have to mock the scope object for the poll channel since we're calling into angular1 code
|
||||
// We register the onDestroy function to be called later when this object is destroyed
|
||||
'$on': (_, onDestruction) => { this.onDestroyListeners.push(onDestruction) }
|
||||
}, this.getDeploymentStatus(deployment), DEPLOYMENT_POLL_SLEEPTIME);
|
||||
|
||||
pollChannel.start();
|
||||
});
|
||||
}
|
||||
|
||||
// Query each deployment every 5s, and stop polling once it's either available or failed
|
||||
getDeploymentStatus(deployment: DeploymentStatus): (boolean) => void {
|
||||
return (continue_callback: (shouldContinue: boolean) => void) => {
|
||||
const params = {
|
||||
'deployment': deployment.name
|
||||
};
|
||||
|
||||
this.ApiService.scGetDeploymentRolloutStatus(null, params).then((deploymentRollout: DeploymentRollout) => {
|
||||
if (deploymentRollout.status === 'available') {
|
||||
continue_callback(false);
|
||||
|
||||
this.deploymentsCycled++;
|
||||
if (this.deploymentsCycled === this.deploymentsStatus.length) {
|
||||
this.state = 'deployed';
|
||||
}
|
||||
} else if (deploymentRollout.status === 'progressing') {
|
||||
continue_callback(true);
|
||||
deployment.message = deploymentRollout.message;
|
||||
} else { // deployment rollout failed
|
||||
this.state = 'error';
|
||||
continue_callback(false);
|
||||
deployment.message = deploymentRollout.message;
|
||||
this.errorMessage = `Could not cycle deployments: ${deploymentRollout.message}`;
|
||||
|
||||
// Only offer rollback if we loaded/populated a config. (Can't rollback an initial setup)
|
||||
if (this.loadedConfig) {
|
||||
this.rollingBackStatus = 'offer';
|
||||
this.errorMessage = `Could not cycle deployments: ${deploymentRollout.message}`;
|
||||
}
|
||||
}
|
||||
}).catch(err => {
|
||||
continue_callback(false);
|
||||
this.state = 'error';
|
||||
this.errorMessage = `Could not cycle the deployments with the new configuration. Error: ${err.toString()}\
|
||||
Would you like to rollback the deployment to its previous state?`;
|
||||
// Only offer rollback if we loaded/populated a config. (Can't rollback an initial setup)
|
||||
if (this.loadedConfig) {
|
||||
this.rollingBackStatus = 'offer';
|
||||
this.errorMessage = `Could not get deployment information for: ${deployment}`;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
rollbackDeployments(): void {
|
||||
this.rollingBackStatus = 'rolling';
|
||||
const deploymentNames: string[] = this.deploymentsStatus.map(dep => dep.name);
|
||||
|
||||
this.ApiService.scRollbackDeployments({ deploymentNames }).then(() => {
|
||||
this.state = 'rolledBackWarning';
|
||||
this.rollingBackStatus = 'none';
|
||||
}).catch(err => {
|
||||
this.rollingBackStatus = 'none';
|
||||
this.state = 'error';
|
||||
this.errorMessage = `Could not cycle the deployments back to their previous states. Please contact support: ${err.toString()}`;
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
.kube-deploy-modal__body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 15px;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.kube-deploy-modal__list {
|
||||
padding-top: 10px;
|
||||
padding-left: 15px;
|
||||
margin-bottom: 15px;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.kube-deploy-modal__list-header {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.kube-deploy-modal__list-item {
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
.kube-deploy-modal__list-item i {
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.kube-deploy-modal__body .btn {
|
||||
align-self: center;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.kube-deploy-modal .rollback {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.kube-deploy-modal .co-alert.co-alert-warning {
|
||||
padding: 25px;
|
||||
display: flex;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.kube-deploy-modal .co-alert.co-alert-warning:before {
|
||||
position: static;
|
||||
padding-right: 15px;
|
||||
}
|
Reference in a new issue