You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

832 lines
24 KiB

4 years ago
<template>
<card class="card-user" style="max-height:100%">
<div class="author">
<img class="avatar border-white" src="@/assets/img/docker.png" alt="...">
</div>
<b-container fluid class="bv-example-row">
4 years ago
<div class="row text-center">
<div class="col-12">
<b>Swarmlab hybrid Deploy</b>
</div>
</div>
<br>
4 years ago
<b-row>
<b-col cols="8">
<ValidationProvider
ref="hybrid_image"
name="Image"
rules="required|alpha_num_image"
v-slot="{ errors, ariaMsg, ariaInput, valid, invalid }"
>
<div class="input-group input-group-sm sm-3">
<input type="text"
class="form-control"
v-bind:class="{'is-valid': isDeployValid.image == 1, 'is-invalid': isDeployValid.image == 2}"
name="Image"
aria-label="Small"
aria-describedby="inputGroup-sizing-sm"
placeholder="Image"
v-model="deploy.image"
v-on:keyup="isValid('image')"
>
<div class="input-group-append">
<button
class="ti-info btn btn-outline-secondary"
round
type="button"
@click="showInfo('image')"
>
</button>
</div>
</div>
<span class="hybrid-field-error">{{ errors[0] }}</span>
</ValidationProvider>
</b-col>
<b-col cols="4">
<ValidationProvider
ref="hybrid_stackname"
name="StackName"
rules="required|alpha_num_name"
v-slot="{ errors, ariaMsg, ariaInput, valid, invalid }"
>
<div class="input-group input-group-sm sm-3">
<input type="text"
name="StackName"
class="form-control"
v-bind:class="{'is-valid': isDeployValid.stackname == 1, 'is-invalid': isDeployValid.stackname == 2}"
aria-label="Small"
aria-describedby="inputGroup-sizing-sm"
placeholder="StackName"
v-model="deploy.stackname"
v-on:keyup="isValid('stackname')"
>
<div class="input-group-append">
<button
class="ti-info btn btn-outline-secondary"
round
type="button"
@click="showInfo('stackname')">
</button>
</div>
</div>
<span class="hybrid-field-error">{{ errors[0] }}</span>
</ValidationProvider>
</b-col>
</b-row>
<b-row>
<b-col cols="8">
<ValidationProvider
ref="hybrid_name"
name="Name"
rules="required|alpha_num_name"
v-slot="{ errors, ariaMsg, ariaInput, valid, invalid }"
>
<div class="input-group input-group-sm sm-3">
<input type=HybridError
name="Name"
class="form-control"
v-bind:class="{'is-valid': isDeployValid.name == 1, 'is-invalid': isDeployValid.name == 2}"
aria-label="Small" aria-describedby="inputGroup-sizing-sm"
placeholder="Name"
v-model="deploy.name"
v-on:keyup="isValid('name')"
>
<div class="input-group-append">
<button
class="ti-info btn btn-outline-secondary"
round
type="button"
@click="showInfo('name')">
</button>
</div>
</div>
<span class="hybrid-field-error">{{ errors[0] }}</span>
</ValidationProvider>
</b-col>
<b-col cols="4">
<ValidationProvider
ref="hybrid_network"
name="Network"
rules="required|alpha_num_name"
v-slot="{ errors, ariaMsg, ariaInput, valid, invalid }"
>
<div class="input-group input-group-sm sm-3">
<input type="text"
name="Network"
class="form-control"
v-bind:class="{'is-valid': isDeployValid.network == 1, 'is-invalid': isDeployValid.network == 2}"
aria-label="Small" aria-describedby="inputGroup-sizing-sm"
placeholder="Network Name"
v-model="deploy.network"
v-on:keyup="isValid('network')"
>
<div class="input-group-append">
<button
class="ti-info btn btn-outline-secondary"
round
type="button"
@click="showInfo('network')">
</button>
</div>
</div>
<span class="hybrid-field-error">{{ errors[0] }}</span>
</ValidationProvider>
</b-col>
</b-row>
<b-row>
<b-col cols="4">
<ValidationProvider
ref="hybrid_cpu"
name="Cpu"
rules="required|alpha_num_cpu"
v-slot="{ errors, ariaMsg, ariaInput, valid, invalid }"
>
<div class="input-group input-group-sm sm-3">
<input type="text"
name="Cpu"
class="form-control"
v-bind:class="{'is-valid': isDeployValid.cpu == 1, 'is-invalid': isDeployValid.cpu == 2}"
aria-label="Small" aria-describedby="inputGroup-sizing-sm"
placeholder="Limit CPU e.g. 0.50"
v-model="deploy.cpu"
v-on:keyup="isValid('cpu')"
>
<div class="input-group-append">
<button
class="ti-info btn btn-outline-secondary"
round
type="button"
@click="showInfo('cpu')"
>
</button>
</div>
</div>
<span class="hybrid-field-error">{{ errors[0] }}</span>
</ValidationProvider>
</b-col>
<b-col cols="4">
<ValidationProvider
ref="hybrid_memory"
name="Memory"
rules="required|alpha_num_memory"
v-slot="{ errors, ariaMsg, ariaInput, valid, invalid }"
>
<div class="input-group input-group-sm sm-3">
<input type="text"
name="Memory"
class="form-control"
v-bind:class="{'is-valid': isDeployValid.memory == 1, 'is-invalid': isDeployValid.memory == 2}"
aria-label="Small" aria-describedby="inputGroup-sizing-sm"
placeholder="Limit Memory e.g. 500"
v-model="deploy.memory"
v-on:keyup="isValid('memory')"
>
<div class="input-group-append">
<button
class="ti-info btn btn-outline-secondary"
round
type="button"
@click="showInfo('memory')">
</button>
</div>
</div>
<span class="hybrid-field-error">{{ errors[0] }}</span>
</ValidationProvider>
</b-col>
<b-col cols="4">
<ValidationProvider
ref="hybrid_networkport"
name="Networkport"
rules="mybetween:1,65535"
v-slot="{ errors, ariaMsg, ariaInput, valid, invalid }"
>
<div class="input-group input-group-sm sm-3">
<input type="text"
name="Networkport"
class="form-control"
v-bind:class="{'is-valid': isDeployValid.networkport == 1, 'is-invalid': isDeployValid.networkport == 2}"
aria-label="Small" aria-describedby="inputGroup-sizing-sm"
placeholder="Net Port (inside the container)"
v-model="deploy.networkport"
v-on:keyup="isValid('networkport')"
>
<div class="input-group-append">
<button
class="ti-info btn btn-outline-secondary"
round
type="button"
@click="showInfo('networkport')">
</button>
</div>
</div>
<span class="hybrid-field-error">{{ errors[0] }}</span>
</ValidationProvider>
</b-col>
</b-row>
4 years ago
<!-- --------------------------------------------------------- -->
<!-- -------- lab url ----------------------------------- -->
<!-- --------------------------------------------------------- -->
<b-row>
<b-col cols="12">
<ValidationProvider
ref="hybrid_url"
name="Url"
rules="required|alpha_url"
v-slot="{ errors, ariaMsg, ariaInput, valid, invalid }"
>
<div class="input-group input-group-sm sm-3">
<input type="text"
class="form-control"
v-bind:class="{'is-valid': isDeployValid.url == 1, 'is-invalid': isDeployValid.url == 2}"
name="Url"
aria-label="Small"
aria-describedby="inputGroup-sizing-sm"
placeholder="link to README file (see info button for more)"
v-model="deploy.url"
v-on:keyup="isValid('url')"
>
<div class="input-group-append">
<button
class="ti-info btn btn-outline-secondary"
round
type="button"
@click="showInfo('url')"
>
</button>
</div>
</div>
<span class="hybrid-field-error">{{ errors[0] }}</span>
</ValidationProvider>
</b-col>
</b-row>
4 years ago
<!-- --------------------------------------------------------- -->
<!-- -------- lab startdate ----------------------------------- -->
<!-- --------------------------------------------------------- -->
<b-row>
<b-col cols="6">
<div class="input-group input-group-sm mb-3">
<div class="input-group-prepend">
<button class="btn btn-outline-secondary"
type="button"
>Start date </button>
</div>
<date-picker
type="date"
autocomplete="off"
v-model="deploy.startdate"
format="YYYY-MM-DD"
lang="en"
>
</date-picker>
</div>
</b-col>
<!-- --------------------------------------------------------- -->
<!-- -------- lab starttime ----------------------------------- -->
<!-- --------------------------------------------------------- -->
<b-col cols="6">
<div class="input-group input-group-sm mb-3">
<div class="input-group-prepend">
<button class="btn btn-outline-secondary"
type="button"
>Start time</button>
</div>
<date-picker
type="time"
:format="'HH:mm'"
v-model="deploy.starttime"
show-hour
show-minute
:time-picker-options="timePickerOptions"
lang="en"
>
</date-picker>
</div>
</b-col>
</b-row>
<!-- --------------------------------------------------------- -->
<!-- -------- lab enddate ----------------------------------- -->
<!-- --------------------------------------------------------- -->
<b-row>
<b-col cols="6">
<div class="input-group input-group-sm mb-3">
<div class="input-group-prepend">
<button class="btn btn-outline-secondary"
type="button"
>End date </button>
</div>
<date-picker
type="date"
autocomplete="off"
v-model="deploy.enddate"
format="YYYY-MM-DD"
lang="en"
>
</date-picker>
</div>
</b-col>
<!-- --------------------------------------------------------- -->
<!-- -------- lab endtime ----------------------------------- -->
<!-- --------------------------------------------------------- -->
<b-col cols="6">
<div class="input-group input-group-sm mb-3">
<div class="input-group-prepend">
<button class="btn btn-outline-secondary"
type="button"
>End time</button>
</div>
<date-picker
type="time"
:format="'HH:mm'"
v-model="deploy.endtime"
show-hour
show-minute
:time-picker-options="timePickerOptions"
lang="en"
>
</date-picker>
</div>
</b-col>
</b-row>
<b-row>
<b-col cols="6">
<div class="input-group input-group-sm sm-3">
<div class="input-group-prepend">
<button
class="ti-cloud-up btn btn-outline-success"
round
type="button"
title="Deploy"
@click="add_deploy()"
> Deploy
</button>
</div>
</div>
</b-col>
<b-col cols="2">
</b-col>
<b-col cols="4">
<div class="custom-control custom-switch">
<input type="checkbox" class="custom-control-input" id="customStack"
v-model="deploy.usersjoin"
value="true"
unchecked-value="false"
>
<label class="custom-control-label" for="customStack">Users can join the lab instance</label>
</div>
</b-col>
</b-row>
</b-container>
</card>
</template>
<script>
import store from '@/store/index'
import {mapState, mapGetters, mapActions,dispatch} from 'vuex'
import Vue from 'vue'
import DatePicker from 'vue2-datepicker'
import card from '@/components/Card.vue'
import {ApiConfig} from "@/config/index";
import { ValidationObserver, ValidationProvider, extend } from 'vee-validate';
import { required, alpha_num, between} from 'vee-validate/dist/rules';
import 'vue2-datepicker/index.css';
// No message specified.
extend('alpha_num', alpha_num);
extend('mybetween', between);
extend('mybetween', {
message: 'The {_field_} field must be a number: 1-65535'
});
//extend('alpha_num_image', alpha_num);
extend('alpha_num_image', value => {
var regex = new RegExp(/^[A-Za-z0-9\:\.\-\_\/]+$/, 'i');
if(regex.test(value)){
return true;
}
return 'The {_field_} field may contain alphabetic characters, numbers, colons, hyphens, slashes, dots and underscores'
});
extend('alpha_num_name', value => {
var regex = new RegExp(/^[A-Za-z0-9]+$/, 'i');
if(regex.test(value)){
return true;
}
return 'The {_field_} field may contain alphabetic characters and numbers'
});
extend('alpha_num_cpu', value => {
//var regex = new RegExp(/^(?:\d{1})?(?:\.\d{1})?$/);
var regex = new RegExp(/^(?!0\d)\d+(?:\.\d{1})?$/);
if(regex.test(value)){
return true;
}
return 'The {_field_} field may contain numbers and dots e.g 0.5'
});
extend('alpha_num_memory', value => {
var regex = new RegExp(/^[0-9]+$/, 'i');
if(regex.test(value)){
return true;
}
return 'The {_field_} field may contain numbers e.g 500'
});
4 years ago
extend('alpha_url', value => {
var regex = new RegExp(/https?:\/\/(git\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,4}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/,'i');
//var regex = new RegExp(/^[0-9]+$/, 'i');
if(regex.test(value)){
return true;
}
return 'The {_field_} field may contain url'
});
4 years ago
// Override the default message.
extend('required', {
...required,
message: 'This field is required'
});
export default {
components: {
DatePicker,
ValidationProvider,
card
},
props: {
},
data() {
return{
isHybridError: false,
isHybridSuccess: false,
token: '',
timePickerOptions:{
start: '00:00',
step: '00:30',
end: '23:30'
},
deploy:{
"usersjoin":true
},
isDeployValid:{
"image":3,
"name":3,
"stackname":3,
"network":3,
4 years ago
//"networkport":3,
4 years ago
"url":3,
4 years ago
"cpu":3,
"memory":3
},
isDeployError:{
}
}
},
mounted() {
},
created() {
var url_string = window.location.href
var url = new URL(url_string);
this.token = url.searchParams.get("token");
4 years ago
//console.log("token "+ this.token);
4 years ago
},
beforeDestroy () {
},
computed: {
},
methods: {
/*
async isValidate (event) {
this.$nextTick(() => {
//await this.isValid(event)
4 years ago
//console.log("keywords value: " + this.deploy.image);
4 years ago
});
},
*/
async add_deploy(){
4 years ago
var o = Object.entries(this.deploy)
var pvalid = new Promise((resolve, reject) => {
o.forEach((value, index, array) => {
this.isValid(array[index][0])
if (index === array.length -1) resolve();
});
});
pvalid.then(() => {
(async () => {
var isAllValid = true
for (const [key, value] of Object.entries(this.isDeployValid)) {
4 years ago
//console.log(`${key}: ${value}`);
4 years ago
if(value != 1){
isAllValid = false
}
4 years ago
}
4 years ago
if(isAllValid){
var log = await store.dispatch("pipelineLLO/adddeploy",{
token:this.token,
deploy:this.deploy
})
this.$root.$emit('hybrid_refresh_bootstrap_view')
4 years ago
console.log('ok')
var info = '<h5>The deploy process is started</h5> Please wait for it to finish before trying again! <br><br>See also in "Manage your deployments" table'
this.$swal({
type: 'Info',
title: 'Info!',
icon:'info',
html: info,
showCloseButton: true,
showLoaderOnConfirm: false,
allowOutsideClick: false,
cancelButtonText: 'No, cancel!',
showCancelButton: false,
showLoaderOnConfirm: false,
reverseButtons: true,
focusCancel: true,
confirmButtonText: 'Ok!'
})
4 years ago
}else{
var info = "Missing required fields"
this.$swal({
type: 'Info',
title: 'Info!',
icon:'info',
html: info,
showCloseButton: true,
showLoaderOnConfirm: false,
allowOutsideClick: false,
cancelButtonText: 'No, cancel!',
showCancelButton: false,
showLoaderOnConfirm: false,
reverseButtons: true,
focusCancel: true,
confirmButtonText: 'Ok!'
})
4 years ago
4 years ago
}
})();
});
4 years ago
},
async showInfo(action){
if(action == 'image' ){
var info=`<h5>To start, we need to have a <b>docker image </b> <br>
<br> We have built some. You can find it here: Menu "Images"</h5>
<h6>More Info here: <a href="https://en.wikipedia.org/wiki/Docker_(software)" target="new">Wikipedia</a> , <a href="https://www.docker.com/resources/what-container" target="new">Docker</a> </h6>
<br>
<br>
The field may contain alphabetic characters, numbers, colons, hyphens, slashes, dots and underscores
`
}else if(action == 'stackname'){
var info=`<h5>To start, we need to have a <b>Stack Name </b> <br>
<br> This will bring up all the services, volumes, networks and everything else <br> in an isolated environment.
</h6>
<br>
<br>
The field may contain alphabetic characters and numbers
`
}else if(action == 'network'){
var info=`<h5>To start, we need to have a <b>Network </b> <br>
<br> This will bring up all the services, volumes, networks and everything else <br> in an isolated network environment.
<br>
<br>
Services running inside any of this networks containers have access (not limited by any firewall) to all other services.
<br>
</h5>
<br>
<br>
The field may contain alphabetic characters and numbers
4 years ago
`
}else if(action == 'url'){
var info=`<h5><b>Git Repo url </b></h5>
<br> The location of the file describing your service. (most comonly README)</br>
<br>
</br>
</h5>
<br>
<br>
The field may contain any valid url BUT it must be under https://git.swarmlab.io
4 years ago
`
}else if(action == 'networkport'){
var info=`<h5><b>Network Port </b></h5>
<br>
<h5>
By default, when you create a container, <b>it does not publish any of its ports to the outside world. </b>
<br>
To make a port available to Services which are not connected to the Stack network, we use this port.
</h5>
<br>
<h5>
<u>
To make a service available we have to know the port number used by these service inside the container.
</u>
<br>
<b>Please insert that here.</b>
</h5>
<br>
<br>
(The port for outside connections is automatically generated and you dont have to worry about it!)
<br>
<br>
The field may contain numbers
`
}else if(action == 'cpu'){
var info=`<h5>To start, we need to have a <b>CPU </b> Limit <br>
<br> Limit the specific CPUs or cores a container can use
</h6>
<br>
<br>
The field may contain numbers and dots e.g 0.5
`
}else if(action == 'memory'){
var info=`<h5>To start, we need to have a <b>Memory </b> Limit
<br> <br> Limit the specific Memoty a container can use <br>
e.g 200
<br>
(MB)
</h6>
<br>
<br>
<h6>
<i>
It is important <b>not</b> to allow a running container to consume too much of the host machines memory.
</i>
</h6>
<br>
<br>
The field may contain numbers
`
}else if(action == 'name'){
var info=`<h5>To start, we need to have a <b>Name </b><br>
This name is only for your own use. The system completely disregards it!
</h5>
<br>
<br>
The field may contain alphabetic characters and numbers
`
}
this.$swal({
type: 'Info',
title: 'Info!',
icon:'info',
html: info,
showCloseButton: true,
showLoaderOnConfirm: false,
allowOutsideClick: false,
cancelButtonText: 'No, cancel!',
showCancelButton: false,
showLoaderOnConfirm: false,
reverseButtons: true,
focusCancel: true,
confirmButtonText: 'Ok!'
})
},
async isValid(f){
// ------------
// validate
// ------------
if(f == 'image'){
4 years ago
//console.log(this.deploy.image)
4 years ago
var field = await this.$refs.hybrid_image.validate();
if(field.valid == true){
this.isDeployValid.image = 1
}else{
this.isDeployValid.image = 2
}
//console.log(JSON.stringify(f_image))
//console.log(this.isDeployValid.image)
}else if(f == 'stackname'){
var field = await this.$refs.hybrid_stackname.validate();
if(field.valid == true){
this.isDeployValid.stackname = 1
}else{
this.isDeployValid.stackname = 2
}
}else if(f == 'network'){
var field = await this.$refs.hybrid_network.validate();
if(field.valid == true){
this.isDeployValid.network = 1
}else{
this.isDeployValid.network = 2
}
4 years ago
}else if(f == 'url'){
var field = await this.$refs.hybrid_url.validate();
if(field.valid == true){
this.isDeployValid.url = 1
}else{
this.isDeployValid.url = 2
}
4 years ago
}else if(f == 'networkport'){
var field = await this.$refs.hybrid_networkport.validate();
4 years ago
//console.log('port '+JSON.stringify(field))
4 years ago
if(field.valid == true){
this.isDeployValid.networkport = 1
}else{
this.isDeployValid.networkport = 2
}
}else if(f == 'cpu'){
var field = await this.$refs.hybrid_cpu.validate();
if(field.valid == true){
this.isDeployValid.cpu = 1
}else{
this.isDeployValid.cpu = 2
}
}else if(f == 'memory'){
var field = await this.$refs.hybrid_memory.validate();
if(field.valid == true){
this.isDeployValid.memory = 1
}else{
this.isDeployValid.memory = 2
}
}else if(f == 'name'){
var field = await this.$refs.hybrid_name.validate();
if(field.valid == true){
this.isDeployValid.name = 1
}else{
this.isDeployValid.name = 2
}
}
}
},
actions: {
}
};
</script>
<style>
.flex-fixed-width-item {
flex: 0 0 100px;
}
.modalinfo {
z-index: 10000000 !important;
position:fixed;
}
/* a container with flex-direction column */
.vue-notifyjs.notifications{
.alert{
z-index: 100;
}
.list-move {
transition: transform 0.3s, opacity 0.4s;
}
.list-item {
display: inline-block;
margin-right: 10px;
}
.list-enter-active {
transition: transform 0.2s ease-in, opacity 0.4s ease-in;
}
.list-leave-active {
transition: transform 1s ease-out, opacity 0.4s ease-out;
}
.list-enter {
opacity: 0;
transform: scale(1.1);
}
.list-leave-to {
opacity: 0;
transform: scale(1.2, 0.7);
}
}
pre {
//background-color: rgb(255, 247, 229);
background-color: #eff0f1;
border: 1px solid blue;
//white-space: pre-line;
}
.hybrid-field-error {
color: red;
}
.hybrid-error {
color: red;
}
</style>