Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
9a322940
Commit
9a322940
authored
Apr 26, 2019
by
GitLab Bot
Browse files
Options
Browse Files
Download
Plain Diff
Automatic merge of gitlab-org/gitlab-ce master
parents
27d74bbe
c1d93721
Changes
12
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
492 additions
and
239 deletions
+492
-239
app/assets/javascripts/clusters/clusters_bundle.js
app/assets/javascripts/clusters/clusters_bundle.js
+13
-18
app/assets/javascripts/clusters/components/application_row.vue
...ssets/javascripts/clusters/components/application_row.vue
+38
-62
app/assets/javascripts/clusters/components/applications.vue
app/assets/javascripts/clusters/components/applications.vue
+42
-42
app/assets/javascripts/clusters/constants.js
app/assets/javascripts/clusters/constants.js
+5
-8
app/assets/javascripts/clusters/services/application_state_machine.js
...avascripts/clusters/services/application_state_machine.js
+141
-0
app/assets/javascripts/clusters/stores/clusters_store.js
app/assets/javascripts/clusters/stores/clusters_store.js
+41
-4
app/assets/javascripts/pages/users/activity_calendar.js
app/assets/javascripts/pages/users/activity_calendar.js
+12
-12
spec/frontend/clusters/clusters_bundle_spec.js
spec/frontend/clusters/clusters_bundle_spec.js
+22
-25
spec/frontend/clusters/components/application_row_spec.js
spec/frontend/clusters/components/application_row_spec.js
+22
-46
spec/frontend/clusters/services/application_state_machine_spec.js
...ntend/clusters/services/application_state_machine_spec.js
+134
-0
spec/frontend/clusters/services/mock_data.js
spec/frontend/clusters/services/mock_data.js
+0
-1
spec/frontend/clusters/stores/clusters_store_spec.js
spec/frontend/clusters/stores/clusters_store_spec.js
+22
-21
No files found.
app/assets/javascripts/clusters/clusters_bundle.js
View file @
9a322940
...
...
@@ -7,15 +7,7 @@ import Flash from '../flash';
import
Poll
from
'
../lib/utils/poll
'
;
import
initSettingsPanels
from
'
../settings_panels
'
;
import
eventHub
from
'
./event_hub
'
;
import
{
APPLICATION_STATUS
,
REQUEST_SUBMITTED
,
REQUEST_FAILURE
,
UPGRADE_REQUESTED
,
UPGRADE_REQUEST_FAILURE
,
INGRESS
,
INGRESS_DOMAIN_SUFFIX
,
}
from
'
./constants
'
;
import
{
APPLICATION_STATUS
,
INGRESS
,
INGRESS_DOMAIN_SUFFIX
}
from
'
./constants
'
;
import
ClustersService
from
'
./services/clusters_service
'
;
import
ClustersStore
from
'
./stores/clusters_store
'
;
import
Applications
from
'
./components/applications.vue
'
;
...
...
@@ -137,7 +129,7 @@ export default class Clusters {
if
(
this
.
showTokenButton
)
this
.
showTokenButton
.
addEventListener
(
'
click
'
,
this
.
showToken
);
eventHub
.
$on
(
'
installApplication
'
,
this
.
installApplication
);
eventHub
.
$on
(
'
upgradeApplication
'
,
data
=>
this
.
upgradeApplication
(
data
));
eventHub
.
$on
(
'
upgradeFailed
'
,
appId
=>
this
.
upgradeFailed
(
appId
));
eventHub
.
$on
(
'
dismissUpgradeSuccess
'
,
appId
=>
this
.
dismissUpgradeSuccess
(
appId
));
eventHub
.
$on
(
'
saveKnativeDomain
'
,
data
=>
this
.
saveKnativeDomain
(
data
));
eventHub
.
$on
(
'
setKnativeHostname
'
,
data
=>
this
.
setKnativeHostname
(
data
));
}
...
...
@@ -146,7 +138,7 @@ export default class Clusters {
if
(
this
.
showTokenButton
)
this
.
showTokenButton
.
removeEventListener
(
'
click
'
,
this
.
showToken
);
eventHub
.
$off
(
'
installApplication
'
,
this
.
installApplication
);
eventHub
.
$off
(
'
upgradeApplication
'
,
this
.
upgradeApplication
);
eventHub
.
$off
(
'
upgradeFailed
'
,
this
.
upgradeFailed
);
eventHub
.
$off
(
'
dismissUpgradeSuccess
'
,
this
.
dismissUpgradeSuccess
);
eventHub
.
$off
(
'
saveKnativeDomain
'
);
eventHub
.
$off
(
'
setKnativeHostname
'
);
}
...
...
@@ -259,12 +251,13 @@ export default class Clusters {
installApplication
(
data
)
{
const
appId
=
data
.
id
;
this
.
store
.
updateAppProperty
(
appId
,
'
requestStatus
'
,
REQUEST_SUBMITTED
);
this
.
store
.
updateAppProperty
(
appId
,
'
requestReason
'
,
null
);
this
.
store
.
updateAppProperty
(
appId
,
'
statusReason
'
,
null
);
this
.
store
.
installApplication
(
appId
);
return
this
.
service
.
installApplication
(
appId
,
data
.
params
).
catch
(()
=>
{
this
.
store
.
updateAppProperty
(
appId
,
'
requestStatus
'
,
REQUEST_FAILURE
);
this
.
store
.
notifyInstallFailure
(
appId
);
this
.
store
.
updateAppProperty
(
appId
,
'
requestReason
'
,
...
...
@@ -275,13 +268,15 @@ export default class Clusters {
upgradeApplication
(
data
)
{
const
appId
=
data
.
id
;
this
.
store
.
updateAppProperty
(
appId
,
'
requestStatus
'
,
UPGRADE_REQUESTED
);
this
.
store
.
updateAppProperty
(
appId
,
'
status
'
,
APPLICATION_STATUS
.
UPDATING
);
this
.
service
.
installApplication
(
appId
,
data
.
params
).
catch
(()
=>
this
.
upgradeFailed
(
appId
));
this
.
store
.
updateApplication
(
appId
);
this
.
service
.
installApplication
(
appId
,
data
.
params
).
catch
(()
=>
{
this
.
store
.
notifyUpdateFailure
(
appId
);
});
}
upgradeFailed
(
appId
)
{
this
.
store
.
updateAppProperty
(
appId
,
'
requestStatus
'
,
UPGRADE_REQUEST_FAILURE
);
dismissUpgradeSuccess
(
appId
)
{
this
.
store
.
acknowledgeSuccessfulUpdate
(
appId
);
}
toggleIngressDomainHelpText
(
ingressPreviousState
,
ingressNewState
)
{
...
...
app/assets/javascripts/clusters/components/application_row.vue
View file @
9a322940
...
...
@@ -8,12 +8,7 @@ import identicon from '../../vue_shared/components/identicon.vue';
import
loadingButton
from
'
../../vue_shared/components/loading_button.vue
'
;
import
UninstallApplicationButton
from
'
./uninstall_application_button.vue
'
;
import
{
APPLICATION_STATUS
,
REQUEST_SUBMITTED
,
REQUEST_FAILURE
,
UPGRADE_REQUESTED
,
}
from
'
../constants
'
;
import
{
APPLICATION_STATUS
}
from
'
../constants
'
;
export
default
{
components
:
{
...
...
@@ -63,10 +58,6 @@ export default {
type
:
String
,
required
:
false
,
},
requestStatus
:
{
type
:
String
,
required
:
false
,
},
requestReason
:
{
type
:
String
,
required
:
false
,
...
...
@@ -76,6 +67,11 @@ export default {
required
:
false
,
default
:
false
,
},
installFailed
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
version
:
{
type
:
String
,
required
:
false
,
...
...
@@ -88,6 +84,21 @@ export default {
type
:
Boolean
,
required
:
false
,
},
updateSuccessful
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
updateFailed
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
updateAcknowledged
:
{
type
:
Boolean
,
required
:
false
,
default
:
true
,
},
installApplicationRequestParams
:
{
type
:
Object
,
required
:
false
,
...
...
@@ -102,21 +113,12 @@ export default {
return
Object
.
values
(
APPLICATION_STATUS
).
includes
(
this
.
status
);
},
isInstalling
()
{
return
(
this
.
status
===
APPLICATION_STATUS
.
SCHEDULED
||
this
.
status
===
APPLICATION_STATUS
.
INSTALLING
||
(
this
.
requestStatus
===
REQUEST_SUBMITTED
&&
!
this
.
statusReason
&&
!
this
.
installed
)
);
return
this
.
status
===
APPLICATION_STATUS
.
INSTALLING
;
},
canInstall
()
{
if
(
this
.
isInstalling
)
{
return
false
;
}
return
(
this
.
status
===
APPLICATION_STATUS
.
NOT_INSTALLABLE
||
this
.
status
===
APPLICATION_STATUS
.
INSTALLABLE
||
this
.
status
===
APPLICATION_STATUS
.
ERROR
||
this
.
isUnknownStatus
);
},
...
...
@@ -137,7 +139,7 @@ export default {
return
!
this
.
installed
||
!
this
.
uninstallable
;
},
installButtonLoading
()
{
return
!
this
.
status
||
this
.
status
===
APPLICATION_STATUS
.
SCHEDULED
||
this
.
isInstalling
;
return
!
this
.
status
||
this
.
isInstalling
;
},
installButtonDisabled
()
{
// Avoid the potential for the real-time data to say APPLICATION_STATUS.INSTALLABLE but
...
...
@@ -168,19 +170,13 @@ export default {
manageButtonLabel
()
{
return
s__
(
'
ClusterIntegration|Manage
'
);
},
hasError
()
{
return
(
!
this
.
isInstalling
&&
(
this
.
status
===
APPLICATION_STATUS
.
ERROR
||
this
.
requestStatus
===
REQUEST_FAILURE
)
);
},
generalErrorDescription
()
{
return
sprintf
(
s__
(
'
ClusterIntegration|Something went wrong while installing %{title}
'
),
{
title
:
this
.
title
,
});
},
versionLabel
()
{
if
(
this
.
up
grad
eFailed
)
{
if
(
this
.
up
dat
eFailed
)
{
return
s__
(
'
ClusterIntegration|Upgrade failed
'
);
}
else
if
(
this
.
isUpgrading
)
{
return
s__
(
'
ClusterIntegration|Upgrading
'
);
...
...
@@ -188,19 +184,6 @@ export default {
return
s__
(
'
ClusterIntegration|Upgraded
'
);
},
upgradeRequested
()
{
return
this
.
requestStatus
===
UPGRADE_REQUESTED
;
},
upgradeSuccessful
()
{
return
this
.
status
===
APPLICATION_STATUS
.
UPDATED
;
},
upgradeFailed
()
{
if
(
this
.
isUpgrading
)
{
return
false
;
}
return
this
.
status
===
APPLICATION_STATUS
.
UPDATE_ERRORED
;
},
upgradeFailureDescription
()
{
return
s__
(
'
ClusterIntegration|Update failed. Please check the logs and try again.
'
);
},
...
...
@@ -211,11 +194,11 @@ export default {
},
upgradeButtonLabel
()
{
let
label
;
if
(
this
.
upgradeAvailable
&&
!
this
.
up
grad
eFailed
&&
!
this
.
isUpgrading
)
{
if
(
this
.
upgradeAvailable
&&
!
this
.
up
dat
eFailed
&&
!
this
.
isUpgrading
)
{
label
=
s__
(
'
ClusterIntegration|Upgrade
'
);
}
else
if
(
this
.
isUpgrading
)
{
label
=
s__
(
'
ClusterIntegration|Updating
'
);
}
else
if
(
this
.
up
grad
eFailed
)
{
}
else
if
(
this
.
up
dat
eFailed
)
{
label
=
s__
(
'
ClusterIntegration|Retry update
'
);
}
...
...
@@ -223,25 +206,18 @@ export default {
},
isUpgrading
()
{
// Since upgrading is handled asynchronously on the backend we need this check to prevent any delay on the frontend
return
(
this
.
status
===
APPLICATION_STATUS
.
UPDATING
||
(
this
.
upgradeRequested
&&
!
this
.
upgradeSuccessful
)
);
return
this
.
status
===
APPLICATION_STATUS
.
UPDATING
;
},
shouldShowUpgradeDetails
()
{
// This method only returns true when;
// Upgrade was successful OR Upgrade failed
// AND new upgrade is unavailable AND version information is present.
return
(
(
this
.
upgradeSuccessful
||
this
.
upgradeFailed
)
&&
!
this
.
upgradeAvailable
&&
this
.
version
);
return
(
this
.
updateSuccessful
||
this
.
updateFailed
)
&&
!
this
.
upgradeAvailable
&&
this
.
version
;
},
},
watch
:
{
status
()
{
if
(
this
.
status
===
APPLICATION_STATUS
.
UPDATE_ERRORED
)
{
eventHub
.
$emit
(
'
upgradeFailed
'
,
this
.
id
);
}
else
if
(
this
.
upgradeRequested
&&
this
.
upgradeSuccessful
)
{
updateSuccessful
()
{
if
(
this
.
updateSuccessful
)
{
this
.
$toast
.
show
(
this
.
upgradeSuccessDescription
);
}
},
...
...
@@ -296,7 +272,7 @@ export default {
</strong>
<slot
name=
"description"
></slot>
<div
v-if=
"
hasError
|| isUnknownStatus"
v-if=
"
installFailed
|| isUnknownStatus"
class=
"cluster-application-error text-danger prepend-top-10"
>
<p
class=
"js-cluster-application-general-error-message append-bottom-0"
>
...
...
@@ -317,10 +293,10 @@ export default {
class=
"form-text text-muted label p-0 js-cluster-application-upgrade-details"
>
{{
versionLabel
}}
<span
v-if=
"up
grad
eSuccessful"
>
to
</span>
<span
v-if=
"up
dat
eSuccessful"
>
to
</span>
<gl-link
v-if=
"up
grad
eSuccessful"
v-if=
"up
dat
eSuccessful"
:href=
"chartRepo"
target=
"_blank"
class=
"js-cluster-application-upgrade-version"
...
...
@@ -329,13 +305,13 @@ export default {
</div>
<div
v-if=
"up
grad
eFailed && !isUpgrading"
v-if=
"up
dat
eFailed && !isUpgrading"
class=
"bs-callout bs-callout-danger cluster-application-banner mt-2 mb-0 js-cluster-application-upgrade-failure-message"
>
{{
upgradeFailureDescription
}}
</div>
<loading-button
v-if=
"upgradeAvailable || up
grad
eFailed || isUpgrading"
v-if=
"upgradeAvailable || up
dat
eFailed || isUpgrading"
class=
"btn btn-primary js-cluster-application-upgrade-button mt-2"
:loading=
"isUpgrading"
:disabled=
"isUpgrading"
...
...
@@ -349,9 +325,9 @@ export default {
role="gridcell"
>
<div
v-if=
"showManageButton"
class=
"btn-group table-action-buttons"
>
<a
:href=
"manageLink"
:class=
"
{ disabled: disabled }" class="btn">
{{
manageButtonLabel
}}
</a>
<a
:href=
"manageLink"
:class=
"
{ disabled: disabled }" class="btn">
{{
manageButtonLabel
}}
</a>
</div>
<div
class=
"btn-group table-action-buttons"
>
<loading-button
...
...
app/assets/javascripts/clusters/components/applications.vue
View file @
9a322940
...
...
@@ -224,9 +224,9 @@ export default {
<p
class=
"append-bottom-0"
>
{{
s__
(
`ClusterIntegration|Choose which applications to install on your Kubernetes cluster.
Helm Tiller is required to install any of the following applications.`
)
Helm Tiller is required to install any of the following applications.`
)
}}
<a
:href=
"helpPath"
>
{{
__
(
'
More information
'
)
}}
</a>
<a
:href=
"helpPath"
>
{{
__
(
'
More information
'
)
}}
</a>
</p>
<div
class=
"cluster-application-list prepend-top-10"
>
...
...
@@ -239,15 +239,16 @@ export default {
:request-status=
"applications.helm.requestStatus"
:request-reason=
"applications.helm.requestReason"
:installed=
"applications.helm.installed"
:install-failed=
"applications.helm.installFailed"
class=
"rounded-top"
title-link=
"https://docs.helm.sh/"
>
<div
slot=
"description"
>
{{
s__
(
`ClusterIntegration|Helm streamlines installing
and managing Kubernetes applications.
Tiller runs inside of your Kubernetes Cluster,
and manages releases of your charts.`
)
and managing Kubernetes applications.
Tiller runs inside of your Kubernetes Cluster,
and manages releases of your charts.`
)
}}
</div>
</application-row>
...
...
@@ -255,7 +256,7 @@ export default {
<div
class=
"svg-container"
v-html=
"helmInstallIllustration"
></div>
{{
s__
(
`ClusterIntegration|You must first install Helm Tiller before
installing the applications below`
)
installing the applications below`
)
}}
</div>
<application-row
...
...
@@ -267,6 +268,7 @@ export default {
:request-status=
"applications.ingress.requestStatus"
:request-reason=
"applications.ingress.requestReason"
:installed=
"applications.ingress.installed"
:install-failed=
"applications.ingress.installFailed"
:disabled=
"!helmInstalled"
title-link=
"https://kubernetes.io/docs/concepts/services-networking/ingress/"
>
...
...
@@ -274,16 +276,14 @@ export default {
<p>
{{
s__
(
`ClusterIntegration|Ingress gives you a way to route
requests to services based on the request host or path,
centralizing a number of services into a single entrypoint.`
)
requests to services based on the request host or path,
centralizing a number of services into a single entrypoint.`
)
}}
</p>
<template
v-if=
"ingressInstalled"
>
<div
class=
"form-group"
>
<label
for=
"ingress-endpoint"
>
{{
s__
(
'
ClusterIntegration|Ingress Endpoint
'
)
}}
</label>
<label
for=
"ingress-endpoint"
>
{{
s__
(
'
ClusterIntegration|Ingress Endpoint
'
)
}}
</label>
<div
v-if=
"ingressExternalEndpoint"
class=
"input-group"
>
<input
id=
"ingress-endpoint"
...
...
@@ -309,8 +309,8 @@ export default {
<p
class=
"form-text text-muted"
>
{{
s__
(
`ClusterIntegration|Point a wildcard DNS to this
generated endpoint in order to access
your application after it has been deployed.`
)
generated endpoint in order to access
your application after it has been deployed.`
)
}}
<a
:href=
"ingressDnsHelpPath"
target=
"_blank"
rel=
"noopener noreferrer"
>
{{
__
(
'
More information
'
)
}}
...
...
@@ -321,10 +321,9 @@ export default {
<p
v-if=
"!ingressExternalEndpoint"
class=
"settings-message js-no-endpoint-message"
>
{{
s__
(
`ClusterIntegration|The endpoint is in
the process of being assigned. Please check your Kubernetes
cluster or Quotas on Google Kubernetes Engine if it takes a long time.`
)
the process of being assigned. Please check your Kubernetes
cluster or Quotas on Google Kubernetes Engine if it takes a long time.`
)
}}
<a
:href=
"ingressDnsHelpPath"
target=
"_blank"
rel=
"noopener noreferrer"
>
{{
__
(
'
More information
'
)
}}
</a>
...
...
@@ -344,6 +343,7 @@ export default {
:request-status=
"applications.cert_manager.requestStatus"
:request-reason=
"applications.cert_manager.requestReason"
:installed=
"applications.cert_manager.installed"
:install-failed=
"applications.cert_manager.installFailed"
:install-application-request-params=
"{ email: applications.cert_manager.email }"
:disabled=
"!helmInstalled"
title-link=
"https://cert-manager.readthedocs.io/en/latest/#"
...
...
@@ -366,15 +366,14 @@ export default {
<p
class=
"form-text text-muted"
>
{{
s__
(
`ClusterIntegration|Issuers represent a certificate authority.
You must provide an email address for your Issuer. `
)
You must provide an email address for your Issuer. `
)
}}
<a
href=
"http://docs.cert-manager.io/en/latest/reference/issuers.html?highlight=email"
target=
"_blank"
rel=
"noopener noreferrer"
>
{{
__
(
'
More information
'
)
}}
</a
>
{{
__
(
'
More information
'
)
}}
</a>
</p>
</div>
</div>
...
...
@@ -391,6 +390,7 @@ export default {
:request-status=
"applications.prometheus.requestStatus"
:request-reason=
"applications.prometheus.requestReason"
:installed=
"applications.prometheus.installed"
:install-failed=
"applications.prometheus.installFailed"
:disabled=
"!helmInstalled"
title-link=
"https://prometheus.io/docs/introduction/overview/"
>
...
...
@@ -408,15 +408,18 @@ export default {
:chart-repo=
"applications.runner.chartRepo"
:upgrade-available=
"applications.runner.upgradeAvailable"
:installed=
"applications.runner.installed"
:install-failed=
"applications.runner.installFailed"
:update-successful=
"applications.runner.updateSuccessful"
:update-failed=
"applications.runner.updateFailed"
:disabled=
"!helmInstalled"
title-link=
"https://docs.gitlab.com/runner/"
>
<div
slot=
"description"
>
{{
s__(`ClusterIntegration|GitLab Runner connects to the
repository and executes CI/CD jobs,
pushing results back and deploying
applications to production.`)
repository and executes CI/CD jobs,
pushing results back and deploying
applications to production.`)
}}
</div>
</application-row>
...
...
@@ -430,6 +433,7 @@ export default {
:request-status=
"applications.jupyter.requestStatus"
:request-reason=
"applications.jupyter.requestReason"
:installed=
"applications.jupyter.installed"
:install-failed=
"applications.jupyter.installFailed"
:install-application-request-params=
"{ hostname: applications.jupyter.hostname }"
:disabled=
"!helmInstalled"
title-link=
"https://jupyterhub.readthedocs.io/en/stable/"
...
...
@@ -438,18 +442,16 @@ export default {
<p>
{{
s__(`ClusterIntegration|JupyterHub, a multi-user Hub, spawns,
manages, and proxies multiple instances of the single-user
Jupyter notebook server. JupyterHub can be used to serve
notebooks to a class of students, a corporate data science group,
or a scientific research group.`)
manages, and proxies multiple instances of the single-user
Jupyter notebook server. JupyterHub can be used to serve
notebooks to a class of students, a corporate data science group,
or a scientific research group.`)
}}
</p>
<
template
v-if=
"ingressExternalEndpoint"
>
<div
class=
"form-group"
>
<label
for=
"jupyter-hostname"
>
{{
s__
(
'
ClusterIntegration|Jupyter Hostname
'
)
}}
</label>
<label
for=
"jupyter-hostname"
>
{{
s__
(
'
ClusterIntegration|Jupyter Hostname
'
)
}}
</label>
<div
class=
"input-group"
>
<input
...
...
@@ -470,7 +472,7 @@ export default {
<p
v-if=
"ingressInstalled"
class=
"form-text text-muted"
>
{{
s__
(
`ClusterIntegration|Replace this with your own hostname if you want.
If you do so, point hostname to Ingress IP Address from above.`
)
If you do so, point hostname to Ingress IP Address from above.`
)
}}
<a
:href=
"ingressDnsHelpPath"
target=
"_blank"
rel=
"noopener noreferrer"
>
{{
__
(
'
More information
'
)
}}
...
...
@@ -490,8 +492,10 @@ export default {
:request-status=
"applications.knative.requestStatus"
:request-reason=
"applications.knative.requestReason"
:installed=
"applications.knative.installed"
:install-failed=
"applications.knative.installFailed"
:install-application-request-params=
"{ hostname: applications.knative.hostname }"
:disabled=
"!helmInstalled"
v-bind=
"applications.knative"
title-link=
"https://github.com/knative/docs"
>
<div
slot=
"description"
>
...
...
@@ -499,7 +503,7 @@ export default {
<p
v-if=
"!rbac"
class=
"rbac-notice bs-callout bs-callout-info append-bottom-0"
>
{{
s__(`ClusterIntegration|You must have an RBAC-enabled cluster
to install Knative.`)
to install Knative.`)
}}
<a
:href=
"helpPath"
target=
"_blank"
rel=
"noopener noreferrer"
>
{{ __('More information') }}
...
...
@@ -510,9 +514,9 @@ export default {
<p>
{{
s__(`ClusterIntegration|Knative extends Kubernetes to provide
a set of middleware components that are essential to build modern,
source-centric, and container-based applications that can run
anywhere: on premises, in the cloud, or even in a third-party data center.`)
a set of middleware components that are essential to build modern,
source-centric, and container-based applications that can run
anywhere: on premises, in the cloud, or even in a third-party data center.`)
}}
</p>
...
...
@@ -523,9 +527,7 @@ export default {
class="form-group col-sm-12 mb-0"
>
<label
for=
"knative-domainname"
>
<strong>
{{
s__
(
'
ClusterIntegration|Knative Domain Name:
'
)
}}
</strong>
<strong>
{{
s__
(
'
ClusterIntegration|Knative Domain Name:
'
)
}}
</strong>
</label>
<input
id=
"knative-domainname"
...
...
@@ -538,9 +540,7 @@ export default {
<
template
v-if=
"knativeInstalled"
>
<div
class=
"form-group col-sm-12 col-md-6 pl-md-0 mb-0 mt-3 mt-md-0"
>
<label
for=
"knative-endpoint"
>
<strong>
{{
s__
(
'
ClusterIntegration|Knative Endpoint:
'
)
}}
</strong>
<strong>
{{
s__
(
'
ClusterIntegration|Knative Endpoint:
'
)
}}
</strong>
</label>
<div
v-if=
"knativeExternalEndpoint"
class=
"input-group"
>
<input
...
...
@@ -583,8 +583,8 @@ export default {
>
{{
s__
(
`ClusterIntegration|The endpoint is in
the process of being assigned. Please check your Kubernetes
cluster or Quotas on Google Kubernetes Engine if it takes a long time.`
)
the process of being assigned. Please check your Kubernetes
cluster or Quotas on Google Kubernetes Engine if it takes a long time.`
)
}}
</p>
...
...
app/assets/javascripts/clusters/constants.js
View file @
9a322940
...
...
@@ -7,6 +7,7 @@ export const CLUSTER_TYPE = {
// These need to match what is returned from the server
export
const
APPLICATION_STATUS
=
{
NO_STATUS
:
null
,
NOT_INSTALLABLE
:
'
not_installable
'
,
INSTALLABLE
:
'
installable
'
,
SCHEDULED
:
'
scheduled
'
,
...
...
@@ -27,17 +28,13 @@ export const APPLICATION_STATUS = {
export
const
APPLICATION_INSTALLED_STATUSES
=
[
APPLICATION_STATUS
.
INSTALLED
,
APPLICATION_STATUS
.
UPDATING
,
APPLICATION_STATUS
.
UPDATED
,
APPLICATION_STATUS
.
UPDATE_ERRORED
,
APPLICATION_STATUS
.
UNINSTALLING
,
APPLICATION_STATUS
.
UNINSTALL_ERRORED
,
];
// These are only used client-side
export
const
REQUEST_SUBMITTED
=
'
request-submitted
'
;
export
const
REQUEST_FAILURE
=
'
request-failur
e
'
;
export
const
UPGRADE_REQUESTED
=
'
upgrade-requested
'
;
export
const
UPGRADE_REQUEST_FAILURE
=
'
upgrade-request-failure
'
;
export
const
UPDATE_EVENT
=
'
updat
e
'
;
export
const
INSTALL_EVENT
=
'
install
'
;
export
const
INGRESS
=
'
ingress
'
;
export
const
JUPYTER
=
'
jupyter
'
;
export
const
KNATIVE
=
'
knative
'
;
...
...
app/assets/javascripts/clusters/services/application_state_machine.js
0 → 100644
View file @
9a322940
import
{
APPLICATION_STATUS
,
UPDATE_EVENT
,
INSTALL_EVENT
}
from
'
../constants
'
;
const
{
NO_STATUS
,
SCHEDULED
,
NOT_INSTALLABLE
,
INSTALLABLE
,
INSTALLING
,
INSTALLED
,
ERROR
,
UPDATING
,
UPDATED
,
UPDATE_ERRORED
,
}
=
APPLICATION_STATUS
;
const
applicationStateMachine
=
{
/* When the application initially loads, it will have `NO_STATUS`
* It will transition from `NO_STATUS` once the async backend call is completed
*/
[
NO_STATUS
]:
{
on
:
{
[
SCHEDULED
]:
{
target
:
INSTALLING
,
},
[
NOT_INSTALLABLE
]:
{
target
:
NOT_INSTALLABLE
,
},
[
INSTALLABLE
]:
{
target
:
INSTALLABLE
,
},
[
INSTALLING
]:
{
target
:
INSTALLING
,
},
[
INSTALLED
]:
{
target
:
INSTALLED
,
},
[
ERROR
]:
{
target
:
INSTALLABLE
,
effects
:
{
installFailed
:
true
,
},
},
[
UPDATING
]:
{
target
:
UPDATING
,
},
[
UPDATED
]:
{
target
:
INSTALLED
,
},
[
UPDATE_ERRORED
]:
{
target
:
INSTALLED
,
effects
:
{
updateFailed
:
true
,
},
},
},
},
[
NOT_INSTALLABLE
]:
{
on
:
{
[
INSTALLABLE
]:
{
target
:
INSTALLABLE
,
},
},
},
[
INSTALLABLE
]:
{
on
:
{
[
INSTALL_EVENT
]:
{
target
:
INSTALLING
,
effects
:
{
installFailed
:
false
,
},
},
// This is possible in artificial environments for E2E testing
[
INSTALLED
]:
{
target
:
INSTALLED
,
},
},
},
[
INSTALLING
]:
{
on
:
{
[
INSTALLED
]:
{
target
:
INSTALLED
,
},
[
ERROR
]:
{
target
:
INSTALLABLE
,
effects
:
{
installFailed
:
true
,
},
},
},
},
[
INSTALLED
]:
{
on
:
{
[
UPDATE_EVENT
]:
{
target
:
UPDATING
,
effects
:
{
updateFailed
:
false
,
updateSuccessful
:
false
,
},
},
},
},
[
UPDATING
]:
{
on
:
{
[
UPDATED
]:
{
target
:
INSTALLED
,
effects
:
{
updateSuccessful
:
true
,
updateAcknowledged
:
false
,
},
},
[
UPDATE_ERRORED
]:
{
target
:
INSTALLED
,
effects
:
{
updateFailed
:
true
,
},
},
},
},
};
/**
* Determines an application new state based on the application current state
* and an event. If the application current state cannot handle a given event,
* the current state is returned.
*
* @param {*} application
* @param {*} event
*/
const
transitionApplicationState
=
(
application
,
event
)
=>
{
const
newState
=
applicationStateMachine
[
application
.
status
].
on
[
event
];
return
newState
?
{
...
application
,
status
:
newState
.
target
,
...
newState
.
effects
,
}
:
application
;
};
export
default
transitionApplicationState
;
app/assets/javascripts/clusters/stores/clusters_store.js
View file @
9a322940
...
...
@@ -7,7 +7,11 @@ import {
CERT_MANAGER
,
RUNNER
,
APPLICATION_INSTALLED_STATUSES
,
APPLICATION_STATUS
,
INSTALL_EVENT
,
UPDATE_EVENT
,
}
from
'
../constants
'
;
import
transitionApplicationState
from
'
../services/application_state_machine
'
;
const
isApplicationInstalled
=
appStatus
=>
APPLICATION_INSTALLED_STATUSES
.
includes
(
appStatus
);
...
...
@@ -15,8 +19,8 @@ const applicationInitialState = {
status
:
null
,
statusReason
:
null
,
requestReason
:
null
,
requestStatus
:
null
,
installed
:
false
,
installFailed
:
false
,
};
export
default
class
ClusterStore
{
...
...
@@ -49,6 +53,9 @@ export default class ClusterStore {
version
:
null
,
chartRepo
:
'
https://gitlab.com/charts/gitlab-runner
'
,
upgradeAvailable
:
null
,
updateAcknowledged
:
true
,
updateSuccessful
:
false
,
updateFailed
:
false
,
},
prometheus
:
{
...
applicationInitialState
,
...
...
@@ -93,6 +100,32 @@ export default class ClusterStore {
this
.
state
.
statusReason
=
reason
;
}
installApplication
(
appId
)
{
this
.
handleApplicationEvent
(
appId
,
INSTALL_EVENT
);
}
notifyInstallFailure
(
appId
)
{
this
.
handleApplicationEvent
(
appId
,
APPLICATION_STATUS
.
ERROR
);
}
updateApplication
(
appId
)
{
this
.
handleApplicationEvent
(
appId
,
UPDATE_EVENT
);
}
notifyUpdateFailure
(
appId
)
{
this
.
handleApplicationEvent
(
appId
,
APPLICATION_STATUS
.
UPDATE_ERRORED
);
}
handleApplicationEvent
(
appId
,
event
)
{
const
currentAppState
=
this
.
state
.
applications
[
appId
];
this
.
state
.
applications
[
appId
]
=
transitionApplicationState
(
currentAppState
,
event
);
}
acknowledgeSuccessfulUpdate
(
appId
)
{
this
.
state
.
applications
[
appId
].
updateAcknowledged
=
true
;
}
updateAppProperty
(
appId
,
prop
,
value
)
{
this
.
state
.
applications
[
appId
][
prop
]
=
value
;
}
...
...
@@ -109,12 +142,16 @@ export default class ClusterStore {
version
,
update_available
:
upgradeAvailable
,
}
=
serverAppEntry
;
const
currentApplicationState
=
this
.
state
.
applications
[
appId
]
||
{};
const
nextApplicationState
=
transitionApplicationState
(
currentApplicationState
,
status
);
this
.
state
.
applications
[
appId
]
=
{
...
(
this
.
state
.
applications
[
appId
]
||
{})
,
status
,
...
currentApplicationState
,
...
nextApplicationState
,
statusReason
,
installed
:
isApplicationInstalled
(
status
),
installed
:
isApplicationInstalled
(
nextApplicationState
.
status
),
// Make sure uninstallable is always false until this feature is unflagged
uninstallable
:
false
,
};
if
(
appId
===
INGRESS
)
{
...
...
app/assets/javascripts/pages/users/activity_calendar.js
View file @
9a322940
...
...
@@ -65,18 +65,18 @@ export default class ActivityCalendar {
this
.
daySize
=
15
;
this
.
daySizeWithSpace
=
this
.
daySize
+
this
.
daySpace
*
2
;
this
.
monthNames
=
[
'
Jan
'
,
'
Feb
'
,
'
Mar
'
,
'
Apr
'
,
'
May
'
,
'
Jun
'
,
'
Jul
'
,
'
Aug
'
,
'
Sep
'
,
'
Oct
'
,
'
Nov
'
,
'
Dec
'
,
__
(
'
Jan
'
)
,
__
(
'
Feb
'
)
,
__
(
'
Mar
'
)
,
__
(
'
Apr
'
)
,
__
(
'
May
'
)
,
__
(
'
Jun
'
)
,
__
(
'
Jul
'
)
,
__
(
'
Aug
'
)
,
__
(
'
Sep
'
)
,
__
(
'
Oct
'
)
,
__
(
'
Nov
'
)
,
__
(
'
Dec
'
)
,
];
this
.
months
=
[];
this
.
firstDayOfWeek
=
firstDayOfWeek
;
...
...
spec/frontend/clusters/clusters_bundle_spec.js
View file @
9a322940
import
Clusters
from
'
~/clusters/clusters_bundle
'
;
import
{
REQUEST_SUBMITTED
,
REQUEST_FAILURE
,
APPLICATION_STATUS
,
INGRESS_DOMAIN_SUFFIX
,
}
from
'
~/clusters/constants
'
;
import
{
APPLICATION_STATUS
,
INGRESS_DOMAIN_SUFFIX
}
from
'
~/clusters/constants
'
;
import
MockAdapter
from
'
axios-mock-adapter
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
{
loadHTMLFixture
}
from
'
helpers/fixtures
'
;
import
{
setTestTimeout
}
from
'
helpers/timeout
'
;
import
$
from
'
jquery
'
;
const
{
INSTALLING
,
INSTALLABLE
,
INSTALLED
,
NOT_INSTALLABLE
}
=
APPLICATION_STATUS
;
describe
(
'
Clusters
'
,
()
=>
{
setTestTimeout
(
1000
);
...
...
@@ -93,7 +90,7 @@ describe('Clusters', () => {
it
(
'
does not show alert when things transition from initial null state to something
'
,
()
=>
{
cluster
.
checkForNewInstalls
(
INITIAL_APP_MAP
,
{
...
INITIAL_APP_MAP
,
helm
:
{
status
:
APPLICATION_STATUS
.
INSTALLABLE
,
title
:
'
Helm Tiller
'
},
helm
:
{
status
:
INSTALLABLE
,
title
:
'
Helm Tiller
'
},
});
const
flashMessage
=
document
.
querySelector
(
'
.js-cluster-application-notice .flash-text
'
);
...
...
@@ -105,11 +102,11 @@ describe('Clusters', () => {
cluster
.
checkForNewInstalls
(
{
...
INITIAL_APP_MAP
,
helm
:
{
status
:
APPLICATION_STATUS
.
INSTALLING
,
title
:
'
Helm Tiller
'
},
helm
:
{
status
:
INSTALLING
,
title
:
'
Helm Tiller
'
},
},
{
...
INITIAL_APP_MAP
,
helm
:
{
status
:
APPLICATION_STATUS
.
INSTALLED
,
title
:
'
Helm Tiller
'
},
helm
:
{
status
:
INSTALLED
,
title
:
'
Helm Tiller
'
},
},
);
...
...
@@ -125,13 +122,13 @@ describe('Clusters', () => {
cluster
.
checkForNewInstalls
(
{
...
INITIAL_APP_MAP
,
helm
:
{
status
:
APPLICATION_STATUS
.
INSTALLING
,
title
:
'
Helm Tiller
'
},
ingress
:
{
status
:
APPLICATION_STATUS
.
INSTALLABLE
,
title
:
'
Ingress
'
},
helm
:
{
status
:
INSTALLING
,
title
:
'
Helm Tiller
'
},
ingress
:
{
status
:
INSTALLABLE
,
title
:
'
Ingress
'
},
},
{
...
INITIAL_APP_MAP
,
helm
:
{
status
:
APPLICATION_STATUS
.
INSTALLED
,
title
:
'
Helm Tiller
'
},
ingress
:
{
status
:
APPLICATION_STATUS
.
INSTALLED
,
title
:
'
Ingress
'
},
helm
:
{
status
:
INSTALLED
,
title
:
'
Helm Tiller
'
},
ingress
:
{
status
:
INSTALLED
,
title
:
'
Ingress
'
},
},
);
...
...
@@ -218,11 +215,11 @@ describe('Clusters', () => {
it
(
'
tries to install helm
'
,
()
=>
{
jest
.
spyOn
(
cluster
.
service
,
'
installApplication
'
).
mockResolvedValueOnce
();
expect
(
cluster
.
store
.
state
.
applications
.
helm
.
requestStatus
).
toEqual
(
null
)
;
cluster
.
store
.
state
.
applications
.
helm
.
status
=
INSTALLABLE
;
cluster
.
installApplication
({
id
:
'
helm
'
});
expect
(
cluster
.
store
.
state
.
applications
.
helm
.
requestStatus
).
toEqual
(
REQUEST_SUBMITTED
);
expect
(
cluster
.
store
.
state
.
applications
.
helm
.
status
).
toEqual
(
INSTALLING
);
expect
(
cluster
.
store
.
state
.
applications
.
helm
.
requestReason
).
toEqual
(
null
);
expect
(
cluster
.
service
.
installApplication
).
toHaveBeenCalledWith
(
'
helm
'
,
undefined
);
});
...
...
@@ -230,11 +227,11 @@ describe('Clusters', () => {
it
(
'
tries to install ingress
'
,
()
=>
{
jest
.
spyOn
(
cluster
.
service
,
'
installApplication
'
).
mockResolvedValueOnce
();
expect
(
cluster
.
store
.
state
.
applications
.
ingress
.
requestStatus
).
toEqual
(
null
)
;
cluster
.
store
.
state
.
applications
.
ingress
.
status
=
INSTALLABLE
;
cluster
.
installApplication
({
id
:
'
ingress
'
});
expect
(
cluster
.
store
.
state
.
applications
.
ingress
.
requestStatus
).
toEqual
(
REQUEST_SUBMITTED
);
expect
(
cluster
.
store
.
state
.
applications
.
ingress
.
status
).
toEqual
(
INSTALLING
);
expect
(
cluster
.
store
.
state
.
applications
.
ingress
.
requestReason
).
toEqual
(
null
);
expect
(
cluster
.
service
.
installApplication
).
toHaveBeenCalledWith
(
'
ingress
'
,
undefined
);
});
...
...
@@ -242,11 +239,11 @@ describe('Clusters', () => {
it
(
'
tries to install runner
'
,
()
=>
{
jest
.
spyOn
(
cluster
.
service
,
'
installApplication
'
).
mockResolvedValueOnce
();
expect
(
cluster
.
store
.
state
.
applications
.
runner
.
requestStatus
).
toEqual
(
null
)
;
cluster
.
store
.
state
.
applications
.
runner
.
status
=
INSTALLABLE
;
cluster
.
installApplication
({
id
:
'
runner
'
});
expect
(
cluster
.
store
.
state
.
applications
.
runner
.
requestStatus
).
toEqual
(
REQUEST_SUBMITTED
);
expect
(
cluster
.
store
.
state
.
applications
.
runner
.
status
).
toEqual
(
INSTALLING
);
expect
(
cluster
.
store
.
state
.
applications
.
runner
.
requestReason
).
toEqual
(
null
);
expect
(
cluster
.
service
.
installApplication
).
toHaveBeenCalledWith
(
'
runner
'
,
undefined
);
});
...
...
@@ -254,13 +251,12 @@ describe('Clusters', () => {
it
(
'
tries to install jupyter
'
,
()
=>
{
jest
.
spyOn
(
cluster
.
service
,
'
installApplication
'
).
mockResolvedValueOnce
();
expect
(
cluster
.
store
.
state
.
applications
.
jupyter
.
requestStatus
).
toEqual
(
null
);
cluster
.
installApplication
({
id
:
'
jupyter
'
,
params
:
{
hostname
:
cluster
.
store
.
state
.
applications
.
jupyter
.
hostname
},
});
expect
(
cluster
.
store
.
state
.
applications
.
jupyter
.
requestStatus
).
toEqual
(
REQUEST_SUBMITTED
)
;
cluster
.
store
.
state
.
applications
.
jupyter
.
status
=
INSTALLABLE
;
expect
(
cluster
.
store
.
state
.
applications
.
jupyter
.
requestReason
).
toEqual
(
null
);
expect
(
cluster
.
service
.
installApplication
).
toHaveBeenCalledWith
(
'
jupyter
'
,
{
hostname
:
cluster
.
store
.
state
.
applications
.
jupyter
.
hostname
,
...
...
@@ -272,16 +268,18 @@ describe('Clusters', () => {
.
spyOn
(
cluster
.
service
,
'
installApplication
'
)
.
mockRejectedValueOnce
(
new
Error
(
'
STUBBED ERROR
'
));
expect
(
cluster
.
store
.
state
.
applications
.
helm
.
requestStatus
).
toEqual
(
null
)
;
cluster
.
store
.
state
.
applications
.
helm
.
status
=
INSTALLABLE
;
const
promise
=
cluster
.
installApplication
({
id
:
'
helm
'
});
expect
(
cluster
.
store
.
state
.
applications
.
helm
.
requestStatus
).
toEqual
(
REQUEST_SUBMITTED
);
expect
(
cluster
.
store
.
state
.
applications
.
helm
.
status
).
toEqual
(
INSTALLING
);
expect
(
cluster
.
store
.
state
.
applications
.
helm
.
requestReason
).
toEqual
(
null
);
expect
(
cluster
.
service
.
installApplication
).
toHaveBeenCalled
();
return
promise
.
then
(()
=>
{
expect
(
cluster
.
store
.
state
.
applications
.
helm
.
requestStatus
).
toEqual
(
REQUEST_FAILURE
);
expect
(
cluster
.
store
.
state
.
applications
.
helm
.
status
).
toEqual
(
INSTALLABLE
);
expect
(
cluster
.
store
.
state
.
applications
.
helm
.
installFailed
).
toBe
(
true
);
expect
(
cluster
.
store
.
state
.
applications
.
helm
.
requestReason
).
toBeDefined
();
});
});
...
...
@@ -315,7 +313,6 @@ describe('Clusters', () => {
});
describe
(
'
toggleIngressDomainHelpText
'
,
()
=>
{
const
{
INSTALLED
,
INSTALLABLE
,
NOT_INSTALLABLE
}
=
APPLICATION_STATUS
;
let
ingressPreviousState
;
let
ingressNewState
;
...
...
spec/frontend/clusters/components/application_row_spec.js
View file @
9a322940
import
Vue
from
'
vue
'
;
import
eventHub
from
'
~/clusters/event_hub
'
;
import
{
APPLICATION_STATUS
,
REQUEST_SUBMITTED
,
REQUEST_FAILURE
,
UPGRADE_REQUESTED
,
}
from
'
~/clusters/constants
'
;
import
{
APPLICATION_STATUS
}
from
'
~/clusters/constants
'
;
import
applicationRow
from
'
~/clusters/components/application_row.vue
'
;
import
mountComponent
from
'
helpers/vue_mount_component_helper
'
;
import
{
DEFAULT_APPLICATION_STATE
}
from
'
../services/mock_data
'
;
...
...
@@ -85,17 +80,6 @@ describe('Application Row', () => {
expect
(
vm
.
installButtonDisabled
).
toEqual
(
false
);
});
it
(
'
has loading "Installing" when APPLICATION_STATUS.SCHEDULED
'
,
()
=>
{
vm
=
mountComponent
(
ApplicationRow
,
{
...
DEFAULT_APPLICATION_STATE
,
status
:
APPLICATION_STATUS
.
SCHEDULED
,
});
expect
(
vm
.
installButtonLabel
).
toEqual
(
'
Installing
'
);
expect
(
vm
.
installButtonLoading
).
toEqual
(
true
);
expect
(
vm
.
installButtonDisabled
).
toEqual
(
true
);
});
it
(
'
has loading "Installing" when APPLICATION_STATUS.INSTALLING
'
,
()
=>
{
vm
=
mountComponent
(
ApplicationRow
,
{
...
DEFAULT_APPLICATION_STATE
,
...
...
@@ -107,18 +91,6 @@ describe('Application Row', () => {
expect
(
vm
.
installButtonDisabled
).
toEqual
(
true
);
});
it
(
'
has loading "Installing" when REQUEST_SUBMITTED
'
,
()
=>
{
vm
=
mountComponent
(
ApplicationRow
,
{
...
DEFAULT_APPLICATION_STATE
,
status
:
APPLICATION_STATUS
.
INSTALLABLE
,
requestStatus
:
REQUEST_SUBMITTED
,
});
expect
(
vm
.
installButtonLabel
).
toEqual
(
'
Installing
'
);
expect
(
vm
.
installButtonLoading
).
toEqual
(
true
);
expect
(
vm
.
installButtonDisabled
).
toEqual
(
true
);
});
it
(
'
has disabled "Installed" when application is installed and not uninstallable
'
,
()
=>
{
vm
=
mountComponent
(
ApplicationRow
,
{
...
DEFAULT_APPLICATION_STATE
,
...
...
@@ -144,10 +116,11 @@ describe('Application Row', () => {
expect
(
installBtn
).
toBe
(
null
);
});
it
(
'
has enabled "Install" when
APPLICATION_STATUS.ERROR
'
,
()
=>
{
it
(
'
has enabled "Install" when
install fails
'
,
()
=>
{
vm
=
mountComponent
(
ApplicationRow
,
{
...
DEFAULT_APPLICATION_STATE
,
status
:
APPLICATION_STATUS
.
ERROR
,
status
:
APPLICATION_STATUS
.
INSTALLABLE
,
installFailed
:
true
,
});
expect
(
vm
.
installButtonLabel
).
toEqual
(
'
Install
'
);
...
...
@@ -159,7 +132,6 @@ describe('Application Row', () => {
vm
=
mountComponent
(
ApplicationRow
,
{
...
DEFAULT_APPLICATION_STATE
,
status
:
APPLICATION_STATUS
.
INSTALLABLE
,
requestStatus
:
REQUEST_FAILURE
,
});
expect
(
vm
.
installButtonLabel
).
toEqual
(
'
Install
'
);
...
...
@@ -251,15 +223,15 @@ describe('Application Row', () => {
expect
(
upgradeBtn
.
innerHTML
).
toContain
(
'
Upgrade
'
);
});
it
(
'
has enabled "Retry update" when
APPLICATION_STATUS.UPDATE_ERRORED
'
,
()
=>
{
it
(
'
has enabled "Retry update" when
update process fails
'
,
()
=>
{
vm
=
mountComponent
(
ApplicationRow
,
{
...
DEFAULT_APPLICATION_STATE
,
status
:
APPLICATION_STATUS
.
UPDATE_ERRORED
,
status
:
APPLICATION_STATUS
.
INSTALLED
,
updateFailed
:
true
,
});
const
upgradeBtn
=
vm
.
$el
.
querySelector
(
'
.js-cluster-application-upgrade-button
'
);
expect
(
upgradeBtn
).
not
.
toBe
(
null
);
expect
(
vm
.
upgradeFailed
).
toBe
(
true
);
expect
(
upgradeBtn
.
innerHTML
).
toContain
(
'
Retry update
'
);
});
...
...
@@ -279,7 +251,8 @@ describe('Application Row', () => {
jest
.
spyOn
(
eventHub
,
'
$emit
'
);
vm
=
mountComponent
(
ApplicationRow
,
{
...
DEFAULT_APPLICATION_STATE
,
status
:
APPLICATION_STATUS
.
UPDATE_ERRORED
,
status
:
APPLICATION_STATUS
.
INSTALLED
,
upgradeAvailable
:
true
,
});
const
upgradeBtn
=
vm
.
$el
.
querySelector
(
'
.js-cluster-application-upgrade-button
'
);
...
...
@@ -308,7 +281,8 @@ describe('Application Row', () => {
vm
=
mountComponent
(
ApplicationRow
,
{
...
DEFAULT_APPLICATION_STATE
,
title
:
'
GitLab Runner
'
,
status
:
APPLICATION_STATUS
.
UPDATE_ERRORED
,
status
:
APPLICATION_STATUS
.
INSTALLED
,
updateFailed
:
true
,
});
const
failureMessage
=
vm
.
$el
.
querySelector
(
'
.js-cluster-application-upgrade-failure-message
'
,
...
...
@@ -324,12 +298,11 @@ describe('Application Row', () => {
vm
=
mountComponent
(
ApplicationRow
,
{
...
DEFAULT_APPLICATION_STATE
,
title
:
'
GitLab Runner
'
,
requestStatus
:
UPGRADE_REQUESTED
,
status
:
APPLICATION_STATUS
.
UPDATE_ERRORED
,
updateSuccessful
:
false
,
});
vm
.
$toast
=
{
show
:
jest
.
fn
()
};
vm
.
status
=
APPLICATION_STATUS
.
UPDATED
;
vm
.
updateSuccessful
=
true
;
vm
.
$nextTick
(()
=>
{
expect
(
vm
.
$toast
.
show
).
toHaveBeenCalledWith
(
'
GitLab Runner upgraded successfully.
'
);
...
...
@@ -342,7 +315,8 @@ describe('Application Row', () => {
const
version
=
'
0.1.45
'
;
vm
=
mountComponent
(
ApplicationRow
,
{
...
DEFAULT_APPLICATION_STATE
,
status
:
APPLICATION_STATUS
.
UPDATED
,
status
:
APPLICATION_STATUS
.
INSTALLED
,
updateSuccessful
:
true
,
version
,
});
const
upgradeDetails
=
vm
.
$el
.
querySelector
(
'
.js-cluster-application-upgrade-details
'
);
...
...
@@ -358,7 +332,8 @@ describe('Application Row', () => {
const
chartRepo
=
'
https://gitlab.com/charts/gitlab-runner
'
;
vm
=
mountComponent
(
ApplicationRow
,
{
...
DEFAULT_APPLICATION_STATE
,
status
:
APPLICATION_STATUS
.
UPDATED
,
status
:
APPLICATION_STATUS
.
INSTALLED
,
updateSuccessful
:
true
,
chartRepo
,
version
,
});
...
...
@@ -372,7 +347,8 @@ describe('Application Row', () => {
const
version
=
'
0.1.45
'
;
vm
=
mountComponent
(
ApplicationRow
,
{
...
DEFAULT_APPLICATION_STATE
,
status
:
APPLICATION_STATUS
.
UPDATE_ERRORED
,
status
:
APPLICATION_STATUS
.
INSTALLED
,
updateFailed
:
true
,
version
,
});
const
upgradeDetails
=
vm
.
$el
.
querySelector
(
'
.js-cluster-application-upgrade-details
'
);
...
...
@@ -388,7 +364,6 @@ describe('Application Row', () => {
vm
=
mountComponent
(
ApplicationRow
,
{
...
DEFAULT_APPLICATION_STATE
,
status
:
null
,
requestStatus
:
null
,
});
const
generalErrorMessage
=
vm
.
$el
.
querySelector
(
'
.js-cluster-application-general-error-message
'
,
...
...
@@ -397,12 +372,13 @@ describe('Application Row', () => {
expect
(
generalErrorMessage
).
toBeNull
();
});
it
(
'
shows status reason when
APPLICATION_STATUS.ERROR
'
,
()
=>
{
it
(
'
shows status reason when
install fails
'
,
()
=>
{
const
statusReason
=
'
We broke it 0.0
'
;
vm
=
mountComponent
(
ApplicationRow
,
{
...
DEFAULT_APPLICATION_STATE
,
status
:
APPLICATION_STATUS
.
ERROR
,
statusReason
,
installFailed
:
true
,
});
const
generalErrorMessage
=
vm
.
$el
.
querySelector
(
'
.js-cluster-application-general-error-message
'
,
...
...
@@ -423,7 +399,7 @@ describe('Application Row', () => {
vm
=
mountComponent
(
ApplicationRow
,
{
...
DEFAULT_APPLICATION_STATE
,
status
:
APPLICATION_STATUS
.
INSTALLABLE
,
requestStatus
:
REQUEST_FAILURE
,
installFailed
:
true
,
requestReason
,
});
const
generalErrorMessage
=
vm
.
$el
.
querySelector
(
...
...
spec/frontend/clusters/services/application_state_machine_spec.js
0 → 100644
View file @
9a322940
import
transitionApplicationState
from
'
~/clusters/services/application_state_machine
'
;
import
{
APPLICATION_STATUS
,
UPDATE_EVENT
,
INSTALL_EVENT
}
from
'
~/clusters/constants
'
;
const
{
NO_STATUS
,
SCHEDULED
,
NOT_INSTALLABLE
,
INSTALLABLE
,
INSTALLING
,
INSTALLED
,
ERROR
,
UPDATING
,
UPDATED
,
UPDATE_ERRORED
,
}
=
APPLICATION_STATUS
;
const
NO_EFFECTS
=
'
no effects
'
;
describe
(
'
applicationStateMachine
'
,
()
=>
{
const
noEffectsToEmptyObject
=
effects
=>
(
typeof
effects
===
'
string
'
?
{}
:
effects
);
describe
(
`current state is
${
NO_STATUS
}
`
,
()
=>
{
it
.
each
`
expectedState | event | effects
${
INSTALLING
}
|
${
SCHEDULED
}
|
${
NO_EFFECTS
}
${
NOT_INSTALLABLE
}
|
${
NOT_INSTALLABLE
}
|
${
NO_EFFECTS
}
${
INSTALLABLE
}
|
${
INSTALLABLE
}
|
${
NO_EFFECTS
}
${
INSTALLING
}
|
${
INSTALLING
}
|
${
NO_EFFECTS
}
${
INSTALLED
}
|
${
INSTALLED
}
|
${
NO_EFFECTS
}
${
INSTALLABLE
}
|
${
ERROR
}
|
${{
installFailed
:
true
}
}
${
UPDATING
}
|
${
UPDATING
}
|
${
NO_EFFECTS
}
${
INSTALLED
}
|
${
UPDATED
}
|
${
NO_EFFECTS
}
${
INSTALLED
}
|
${
UPDATE_ERRORED
}
|
${{
updateFailed
:
true
}
}
`
(
`transitions to $expectedState on $event event and applies $effects`
,
data
=>
{
const
{
expectedState
,
event
,
effects
}
=
data
;
const
currentAppState
=
{
status
:
NO_STATUS
,
};
expect
(
transitionApplicationState
(
currentAppState
,
event
)).
toEqual
({
status
:
expectedState
,
...
noEffectsToEmptyObject
(
effects
),
});
});
});
describe
(
`current state is
${
NOT_INSTALLABLE
}
`
,
()
=>
{
it
.
each
`
expectedState | event | effects
${
INSTALLABLE
}
|
${
INSTALLABLE
}
|
${
NO_EFFECTS
}
`
(
`transitions to $expectedState on $event event and applies $effects`
,
data
=>
{
const
{
expectedState
,
event
,
effects
}
=
data
;
const
currentAppState
=
{
status
:
NOT_INSTALLABLE
,
};
expect
(
transitionApplicationState
(
currentAppState
,
event
)).
toEqual
({
status
:
expectedState
,
...
noEffectsToEmptyObject
(
effects
),
});
});
});
describe
(
`current state is
${
INSTALLABLE
}
`
,
()
=>
{
it
.
each
`
expectedState | event | effects
${
INSTALLING
}
|
${
INSTALL_EVENT
}
|
${{
installFailed
:
false
}
}
${
INSTALLED
}
|
${
INSTALLED
}
|
${
NO_EFFECTS
}
`
(
`transitions to $expectedState on $event event and applies $effects`
,
data
=>
{
const
{
expectedState
,
event
,
effects
}
=
data
;
const
currentAppState
=
{
status
:
INSTALLABLE
,
};
expect
(
transitionApplicationState
(
currentAppState
,
event
)).
toEqual
({
status
:
expectedState
,
...
noEffectsToEmptyObject
(
effects
),
});
});
});
describe
(
`current state is
${
INSTALLING
}
`
,
()
=>
{
it
.
each
`
expectedState | event | effects
${
INSTALLED
}
|
${
INSTALLED
}
|
${
NO_EFFECTS
}
${
INSTALLABLE
}
|
${
ERROR
}
|
${{
installFailed
:
true
}
}
`
(
`transitions to $expectedState on $event event and applies $effects`
,
data
=>
{
const
{
expectedState
,
event
,
effects
}
=
data
;
const
currentAppState
=
{
status
:
INSTALLING
,
};
expect
(
transitionApplicationState
(
currentAppState
,
event
)).
toEqual
({
status
:
expectedState
,
...
noEffectsToEmptyObject
(
effects
),
});
});
});
describe
(
`current state is
${
INSTALLED
}
`
,
()
=>
{
it
.
each
`
expectedState | event | effects
${
UPDATING
}
|
${
UPDATE_EVENT
}
|
${{
updateFailed
:
false
,
updateSuccessful
:
false
}
}
`
(
`transitions to $expectedState on $event event and applies $effects`
,
data
=>
{
const
{
expectedState
,
event
,
effects
}
=
data
;
const
currentAppState
=
{
status
:
INSTALLED
,
};
expect
(
transitionApplicationState
(
currentAppState
,
event
)).
toEqual
({
status
:
expectedState
,
...
effects
,
});
});
});
describe
(
`current state is
${
UPDATING
}
`
,
()
=>
{
it
.
each
`
expectedState | event | effects
${
INSTALLED
}
|
${
UPDATED
}
|
${{
updateSuccessful
:
true
,
updateAcknowledged
:
false
}
}
${
INSTALLED
}
|
${
UPDATE_ERRORED
}
|
${{
updateFailed
:
true
}
}
`
(
`transitions to $expectedState on $event event and applies $effects`
,
data
=>
{
const
{
expectedState
,
event
,
effects
}
=
data
;
const
currentAppState
=
{
status
:
UPDATING
,
};
expect
(
transitionApplicationState
(
currentAppState
,
event
)).
toEqual
({
status
:
expectedState
,
...
effects
,
});
});
});
});
spec/frontend/clusters/services/mock_data.js
View file @
9a322940
...
...
@@ -113,7 +113,6 @@ const DEFAULT_APPLICATION_STATE = {
description
:
'
Some description about this interesting application!
'
,
status
:
null
,
statusReason
:
null
,
requestStatus
:
null
,
requestReason
:
null
,
};
...
...
spec/frontend/clusters/stores/clusters_store_spec.js
View file @
9a322940
...
...
@@ -32,15 +32,6 @@ describe('Clusters Store', () => {
});
describe
(
'
updateAppProperty
'
,
()
=>
{
it
(
'
should store new request status
'
,
()
=>
{
expect
(
store
.
state
.
applications
.
helm
.
requestStatus
).
toEqual
(
null
);
const
newStatus
=
APPLICATION_STATUS
.
INSTALLING
;
store
.
updateAppProperty
(
'
helm
'
,
'
requestStatus
'
,
newStatus
);
expect
(
store
.
state
.
applications
.
helm
.
requestStatus
).
toEqual
(
newStatus
);
});
it
(
'
should store new request reason
'
,
()
=>
{
expect
(
store
.
state
.
applications
.
helm
.
requestReason
).
toEqual
(
null
);
...
...
@@ -68,80 +59,90 @@ describe('Clusters Store', () => {
title
:
'
Helm Tiller
'
,
status
:
mockResponseData
.
applications
[
0
].
status
,
statusReason
:
mockResponseData
.
applications
[
0
].
status_reason
,
requestStatus
:
null
,
requestReason
:
null
,
installed
:
false
,
installFailed
:
false
,
uninstallable
:
false
,
},
ingress
:
{
title
:
'
Ingress
'
,
status
:
mockResponseData
.
applications
[
1
].
status
,
status
:
APPLICATION_STATUS
.
INSTALLABLE
,
statusReason
:
mockResponseData
.
applications
[
1
].
status_reason
,
requestStatus
:
null
,
requestReason
:
null
,
externalIp
:
null
,
externalHostname
:
null
,
installed
:
false
,
installFailed
:
true
,
uninstallable
:
false
,
},
runner
:
{
title
:
'
GitLab Runner
'
,
status
:
mockResponseData
.
applications
[
2
].
status
,
statusReason
:
mockResponseData
.
applications
[
2
].
status_reason
,
requestStatus
:
null
,
requestReason
:
null
,
version
:
mockResponseData
.
applications
[
2
].
version
,
upgradeAvailable
:
mockResponseData
.
applications
[
2
].
update_available
,
chartRepo
:
'
https://gitlab.com/charts/gitlab-runner
'
,
installed
:
false
,
installFailed
:
false
,
updateAcknowledged
:
true
,
updateFailed
:
false
,
updateSuccessful
:
false
,
uninstallable
:
false
,
},
prometheus
:
{
title
:
'
Prometheus
'
,
status
:
mockResponseData
.
applications
[
3
].
status
,
status
:
APPLICATION_STATUS
.
INSTALLABLE
,
statusReason
:
mockResponseData
.
applications
[
3
].
status_reason
,
requestStatus
:
null
,
requestReason
:
null
,
installed
:
false
,
installFailed
:
true
,
uninstallable
:
false
,
},
jupyter
:
{
title
:
'
JupyterHub
'
,
status
:
mockResponseData
.
applications
[
4
].
status
,
statusReason
:
mockResponseData
.
applications
[
4
].
status_reason
,
requestStatus
:
null
,
requestReason
:
null
,
hostname
:
''
,
installed
:
false
,
installFailed
:
false
,
uninstallable
:
false
,
},
knative
:
{
title
:
'
Knative
'
,
status
:
mockResponseData
.
applications
[
5
].
status
,
statusReason
:
mockResponseData
.
applications
[
5
].
status_reason
,
requestStatus
:
null
,
requestReason
:
null
,
hostname
:
null
,
isEditingHostName
:
false
,
externalIp
:
null
,
externalHostname
:
null
,
installed
:
false
,
installFailed
:
false
,
uninstallable
:
false
,
},
cert_manager
:
{
title
:
'
Cert-Manager
'
,
status
:
mockResponseData
.
applications
[
6
].
status
,
status
:
APPLICATION_STATUS
.
INSTALLABLE
,
installFailed
:
true
,
statusReason
:
mockResponseData
.
applications
[
6
].
status_reason
,
requestStatus
:
null
,
requestReason
:
null
,
email
:
mockResponseData
.
applications
[
6
].
email
,
installed
:
false
,
uninstallable
:
false
,
},
},
});
});
describe
.
each
(
APPLICATION_INSTALLED_STATUSES
)(
'
given the current app status is %s
'
,
()
=>
{
describe
.
each
(
APPLICATION_INSTALLED_STATUSES
)(
'
given the current app status is %s
'
,
status
=>
{
it
(
'
marks application as installed
'
,
()
=>
{
const
mockResponseData
=
CLUSTERS_MOCK_DATA
.
GET
[
'
/gitlab-org/gitlab-shell/clusters/2/status.json
'
].
data
;
const
runnerAppIndex
=
2
;
mockResponseData
.
applications
[
runnerAppIndex
].
status
=
APPLICATION_STATUS
.
INSTALLED
;
mockResponseData
.
applications
[
runnerAppIndex
].
status
=
status
;
store
.
updateStateFromServer
(
mockResponseData
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment