Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
李少辉-开发者
gitlab-foss
提交
d048d926
G
gitlab-foss
项目概览
李少辉-开发者
/
gitlab-foss
通知
15
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
G
gitlab-foss
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
d048d926
编写于
5月 02, 2017
作者:
D
Douwe Maan
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'metrics-deployment-history' into 'master'
Metrics deployment history Closes #26914 See merge request !10649
上级
9fce7c8e
7f22256a
变更
21
隐藏空白更改
内联
并排
Showing
21 changed file
with
645 addition
and
108 deletion
+645
-108
app/assets/javascripts/monitoring/constants.js
app/assets/javascripts/monitoring/constants.js
+4
-0
app/assets/javascripts/monitoring/deployments.js
app/assets/javascripts/monitoring/deployments.js
+211
-0
app/assets/javascripts/monitoring/prometheus_graph.js
app/assets/javascripts/monitoring/prometheus_graph.js
+48
-23
app/assets/stylesheets/pages/environments.scss
app/assets/stylesheets/pages/environments.scss
+18
-11
app/controllers/projects/deployments_controller.rb
app/controllers/projects/deployments_controller.rb
+18
-0
app/serializers/deployment_entity.rb
app/serializers/deployment_entity.rb
+2
-0
app/serializers/deployment_serializer.rb
app/serializers/deployment_serializer.rb
+8
-0
app/views/projects/environments/metrics.html.haml
app/views/projects/environments/metrics.html.haml
+1
-1
config/routes/project.rb
config/routes/project.rb
+2
-0
db/migrate/20170327091750_add_created_at_index_to_deployments.rb
...ate/20170327091750_add_created_at_index_to_deployments.rb
+15
-0
db/schema.rb
db/schema.rb
+1
-0
spec/controllers/projects/deployments_controller_spec.rb
spec/controllers/projects/deployments_controller_spec.rb
+42
-0
spec/features/security/project/internal_access_spec.rb
spec/features/security/project/internal_access_spec.rb
+15
-0
spec/features/security/project/private_access_spec.rb
spec/features/security/project/private_access_spec.rb
+15
-0
spec/features/security/project/public_access_spec.rb
spec/features/security/project/public_access_spec.rb
+15
-0
spec/fixtures/api/schemas/deployments.json
spec/fixtures/api/schemas/deployments.json
+58
-0
spec/javascripts/fixtures/environments.rb
spec/javascripts/fixtures/environments.rb
+30
-0
spec/javascripts/fixtures/environments/metrics.html.haml
spec/javascripts/fixtures/environments/metrics.html.haml
+0
-62
spec/javascripts/monitoring/deployments_spec.js
spec/javascripts/monitoring/deployments_spec.js
+133
-0
spec/javascripts/monitoring/prometheus_graph_spec.js
spec/javascripts/monitoring/prometheus_graph_spec.js
+2
-2
spec/serializers/deployment_entity_spec.rb
spec/serializers/deployment_entity_spec.rb
+7
-9
未找到文件。
app/assets/javascripts/monitoring/constants.js
0 → 100644
浏览文件 @
d048d926
import
d3
from
'
d3
'
;
export
const
dateFormat
=
d3
.
time
.
format
(
'
%b %d, %Y
'
);
export
const
timeFormat
=
d3
.
time
.
format
(
'
%H:%M%p
'
);
app/assets/javascripts/monitoring/deployments.js
0 → 100644
浏览文件 @
d048d926
/* global Flash */
import
d3
from
'
d3
'
;
import
{
dateFormat
,
timeFormat
,
}
from
'
./constants
'
;
export
default
class
Deployments
{
constructor
(
width
,
height
)
{
this
.
width
=
width
;
this
.
height
=
height
;
this
.
endpoint
=
document
.
getElementById
(
'
js-metrics
'
).
dataset
.
deploymentEndpoint
;
this
.
createGradientDef
();
}
init
(
chartData
)
{
this
.
chartData
=
chartData
;
this
.
x
=
d3
.
time
.
scale
().
range
([
0
,
this
.
width
]);
this
.
x
.
domain
(
d3
.
extent
(
this
.
chartData
,
d
=>
d
.
time
));
this
.
charts
=
d3
.
selectAll
(
'
.prometheus-graph
'
);
this
.
getData
();
}
getData
()
{
$
.
ajax
({
url
:
this
.
endpoint
,
dataType
:
'
JSON
'
,
})
.
fail
(()
=>
new
Flash
(
'
Error getting deployment information.
'
))
.
done
((
data
)
=>
{
this
.
data
=
data
.
deployments
.
reduce
((
deploymentDataArray
,
deployment
)
=>
{
const
time
=
new
Date
(
deployment
.
created_at
);
const
xPos
=
Math
.
floor
(
this
.
x
(
time
));
time
.
setSeconds
(
this
.
chartData
[
0
].
time
.
getSeconds
());
if
(
xPos
>=
0
)
{
deploymentDataArray
.
push
({
id
:
deployment
.
id
,
time
,
sha
:
deployment
.
sha
,
tag
:
deployment
.
tag
,
ref
:
deployment
.
ref
.
name
,
xPos
,
});
}
return
deploymentDataArray
;
},
[]);
this
.
plotData
();
});
}
plotData
()
{
this
.
charts
.
each
((
d
,
i
)
=>
{
const
svg
=
d3
.
select
(
this
.
charts
[
0
][
i
]);
const
chart
=
svg
.
select
(
'
.graph-container
'
);
const
key
=
svg
.
node
().
getAttribute
(
'
graph-type
'
);
this
.
createLine
(
chart
,
key
);
this
.
createDeployInfoBox
(
chart
,
key
);
});
}
createGradientDef
()
{
const
defs
=
d3
.
select
(
'
body
'
)
.
append
(
'
svg
'
)
.
attr
({
height
:
0
,
width
:
0
,
})
.
append
(
'
defs
'
);
defs
.
append
(
'
linearGradient
'
)
.
attr
({
id
:
'
shadow-gradient
'
,
})
.
append
(
'
stop
'
)
.
attr
({
offset
:
'
0%
'
,
'
stop-color
'
:
'
#000
'
,
'
stop-opacity
'
:
0.4
,
})
.
select
(
this
.
selectParentNode
)
.
append
(
'
stop
'
)
.
attr
({
offset
:
'
100%
'
,
'
stop-color
'
:
'
#000
'
,
'
stop-opacity
'
:
0
,
});
}
createLine
(
chart
,
key
)
{
chart
.
append
(
'
g
'
)
.
attr
({
class
:
'
deploy-info
'
,
})
.
selectAll
(
'
.deploy-info
'
)
.
data
(
this
.
data
)
.
enter
()
.
append
(
'
g
'
)
.
attr
({
class
:
d
=>
`deploy-info-
${
d
.
id
}
-
${
key
}
`
,
transform
:
d
=>
`translate(
${
Math
.
floor
(
d
.
xPos
)
+
1
}
, 0)`
,
})
.
append
(
'
rect
'
)
.
attr
({
x
:
1
,
y
:
0
,
height
:
this
.
height
+
1
,
width
:
3
,
fill
:
'
url(#shadow-gradient)
'
,
})
.
select
(
this
.
selectParentNode
)
.
append
(
'
line
'
)
.
attr
({
class
:
'
deployment-line
'
,
x1
:
0
,
x2
:
0
,
y1
:
0
,
y2
:
this
.
height
+
1
,
});
}
createDeployInfoBox
(
chart
,
key
)
{
chart
.
selectAll
(
'
.deploy-info
'
)
.
selectAll
(
'
.js-deploy-info-box
'
)
.
data
(
this
.
data
)
.
enter
()
.
select
(
d
=>
document
.
querySelector
(
`.deploy-info-
${
d
.
id
}
-
${
key
}
`
))
.
append
(
'
svg
'
)
.
attr
({
class
:
'
js-deploy-info-box hidden
'
,
x
:
3
,
y
:
0
,
width
:
92
,
height
:
60
,
})
.
append
(
'
rect
'
)
.
attr
({
class
:
'
rect-text-metric deploy-info-rect rect-metric
'
,
x
:
1
,
y
:
1
,
rx
:
2
,
width
:
90
,
height
:
58
,
})
.
select
(
this
.
selectParentNode
)
.
append
(
'
g
'
)
.
attr
({
transform
:
'
translate(5, 2)
'
,
})
.
append
(
'
text
'
)
.
attr
({
class
:
'
deploy-info-text text-metric-bold
'
,
})
.
text
(
Deployments
.
refText
)
.
select
(
this
.
selectParentNode
)
.
append
(
'
text
'
)
.
attr
({
class
:
'
deploy-info-text
'
,
y
:
18
,
})
.
text
(
d
=>
dateFormat
(
d
.
time
))
.
select
(
this
.
selectParentNode
)
.
append
(
'
text
'
)
.
attr
({
class
:
'
deploy-info-text text-metric-bold
'
,
y
:
38
,
})
.
text
(
d
=>
timeFormat
(
d
.
time
));
}
static
toggleDeployTextbox
(
deploy
,
key
,
showInfoBox
)
{
d3
.
selectAll
(
`.deploy-info-
${
deploy
.
id
}
-
${
key
}
.js-deploy-info-box`
)
.
classed
(
'
hidden
'
,
!
showInfoBox
);
}
mouseOverDeployInfo
(
mouseXPos
,
key
)
{
if
(
!
this
.
data
)
return
false
;
let
dataFound
=
false
;
this
.
data
.
forEach
((
d
)
=>
{
if
(
d
.
xPos
>=
mouseXPos
-
10
&&
d
.
xPos
<=
mouseXPos
+
10
&&
!
dataFound
)
{
dataFound
=
d
.
xPos
+
1
;
Deployments
.
toggleDeployTextbox
(
d
,
key
,
true
);
}
else
{
Deployments
.
toggleDeployTextbox
(
d
,
key
,
false
);
}
});
return
dataFound
;
}
/* `this` is bound to the D3 node */
selectParentNode
()
{
return
this
.
parentNode
;
}
static
refText
(
d
)
{
return
d
.
tag
?
d
.
ref
:
d
.
sha
.
slice
(
0
,
6
);
}
}
app/assets/javascripts/monitoring/prometheus_graph.js
浏览文件 @
d048d926
...
...
@@ -3,16 +3,20 @@
import
d3
from
'
d3
'
;
import
statusCodes
from
'
~/lib/utils/http_status
'
;
import
{
formatRelevantDigits
}
from
'
~/lib/utils/number_utils
'
;
import
Deployments
from
'
./deployments
'
;
import
'
../lib/utils/common_utils
'
;
import
{
formatRelevantDigits
}
from
'
../lib/utils/number_utils
'
;
import
'
../flash
'
;
import
{
dateFormat
,
timeFormat
,
}
from
'
./constants
'
;
const
prometheusContainer
=
'
.prometheus-container
'
;
const
prometheusParentGraphContainer
=
'
.prometheus-graphs
'
;
const
prometheusGraphsContainer
=
'
.prometheus-graph
'
;
const
prometheusStatesContainer
=
'
.prometheus-state
'
;
const
metricsEndpoint
=
'
metrics.json
'
;
const
timeFormat
=
d3
.
time
.
format
(
'
%H:%M
'
);
const
dayFormat
=
d3
.
time
.
format
(
'
%b %e, %a
'
);
const
bisectDate
=
d3
.
bisector
(
d
=>
d
.
time
).
left
;
const
extraAddedWidthParent
=
100
;
...
...
@@ -36,6 +40,7 @@ class PrometheusGraph {
this
.
width
=
parentContainerWidth
-
this
.
margin
.
left
-
this
.
margin
.
right
;
this
.
height
=
this
.
originalHeight
-
this
.
margin
.
top
-
this
.
margin
.
bottom
;
this
.
backOffRequestCounter
=
0
;
this
.
deployments
=
new
Deployments
(
this
.
width
,
this
.
height
);
this
.
configureGraph
();
this
.
init
();
}
else
{
...
...
@@ -74,6 +79,12 @@ class PrometheusGraph {
$
(
prometheusParentGraphContainer
).
show
();
this
.
transformData
(
metricsResponse
);
this
.
createGraph
();
const
firstMetricData
=
this
.
graphSpecificProperties
[
Object
.
keys
(
this
.
graphSpecificProperties
)[
0
]
].
data
;
this
.
deployments
.
init
(
firstMetricData
);
}
});
}
...
...
@@ -96,6 +107,7 @@ class PrometheusGraph {
.
attr
(
'
width
'
,
this
.
width
+
this
.
margin
.
left
+
this
.
margin
.
right
)
.
attr
(
'
height
'
,
this
.
height
+
this
.
margin
.
bottom
+
this
.
margin
.
top
)
.
append
(
'
g
'
)
.
attr
(
'
class
'
,
'
graph-container
'
)
.
attr
(
'
transform
'
,
`translate(
${
this
.
margin
.
left
}
,
${
this
.
margin
.
top
}
)`
);
const
axisLabelContainer
=
d3
.
select
(
prometheusGraphContainer
)
...
...
@@ -116,6 +128,7 @@ class PrometheusGraph {
.
scale
(
y
)
.
ticks
(
this
.
commonGraphProperties
.
axis_no_ticks
)
.
tickSize
(
-
this
.
width
)
.
outerTickSize
(
0
)
.
orient
(
'
left
'
);
this
.
createAxisLabelContainers
(
axisLabelContainer
,
key
);
...
...
@@ -248,7 +261,8 @@ class PrometheusGraph {
const
d1
=
currentGraphProps
.
data
[
overlayIndex
];
const
evalTime
=
timeValueOverlay
-
d0
.
time
>
d1
.
time
-
timeValueOverlay
;
const
currentData
=
evalTime
?
d1
:
d0
;
const
currentTimeCoordinate
=
currentGraphProps
.
xScale
(
currentData
.
time
);
const
currentTimeCoordinate
=
Math
.
floor
(
currentGraphProps
.
xScale
(
currentData
.
time
));
const
currentDeployXPos
=
this
.
deployments
.
mouseOverDeployInfo
(
currentXCoordinate
,
key
);
const
currentPrometheusGraphContainer
=
`
${
prometheusGraphsContainer
}
[graph-type=
${
key
}
]`
;
const
maxValueFromData
=
d3
.
max
(
currentGraphProps
.
data
.
map
(
metricValue
=>
metricValue
.
value
));
const
maxMetricValue
=
currentGraphProps
.
yScale
(
maxValueFromData
);
...
...
@@ -256,13 +270,12 @@ class PrometheusGraph {
// Clear up all the pieces of the flag
d3
.
selectAll
(
`
${
currentPrometheusGraphContainer
}
.selected-metric-line`
).
remove
();
d3
.
selectAll
(
`
${
currentPrometheusGraphContainer
}
.circle-metric`
).
remove
();
d3
.
selectAll
(
`
${
currentPrometheusGraphContainer
}
.rect-text-metric`
).
remove
();
d3
.
selectAll
(
`
${
currentPrometheusGraphContainer
}
.text-metric`
).
remove
();
d3
.
selectAll
(
`
${
currentPrometheusGraphContainer
}
.rect-text-metric:not(.deploy-info-rect)`
).
remove
();
const
currentChart
=
d3
.
select
(
currentPrometheusGraphContainer
).
select
(
'
g
'
);
currentChart
.
append
(
'
line
'
)
.
attr
(
'
class
'
,
'
selected-metric-line
'
)
.
attr
({
class
:
`
${
currentDeployXPos
?
'
hidden
'
:
''
}
selected-metric-line`
,
x1
:
currentTimeCoordinate
,
y1
:
currentGraphProps
.
yScale
(
0
),
x2
:
currentTimeCoordinate
,
...
...
@@ -272,33 +285,45 @@ class PrometheusGraph {
currentChart
.
append
(
'
circle
'
)
.
attr
(
'
class
'
,
'
circle-metric
'
)
.
attr
(
'
fill
'
,
currentGraphProps
.
line_color
)
.
attr
(
'
cx
'
,
currentTimeCoordinate
)
.
attr
(
'
cx
'
,
current
DeployXPos
||
current
TimeCoordinate
)
.
attr
(
'
cy
'
,
currentGraphProps
.
yScale
(
currentData
.
value
))
.
attr
(
'
r
'
,
this
.
commonGraphProperties
.
circle_radius_metric
);
if
(
currentDeployXPos
)
return
;
// The little box with text
const
rectTextMetric
=
currentChart
.
append
(
'
g
'
)
.
attr
(
'
class
'
,
'
rect-text-metric
'
)
.
attr
(
'
translate
'
,
`(
${
currentTimeCoordinate
}
,
${
currentGraphProps
.
yScale
(
currentData
.
value
)}
)`
);
const
rectTextMetric
=
currentChart
.
append
(
'
svg
'
)
.
attr
({
class
:
'
rect-text-metric
'
,
x
:
currentTimeCoordinate
,
y
:
0
,
});
rectTextMetric
.
append
(
'
rect
'
)
.
attr
(
'
class
'
,
'
rect-metric
'
)
.
attr
(
'
x
'
,
currentTimeCoordinate
+
10
)
.
attr
(
'
y
'
,
maxMetricValue
)
.
attr
(
'
width
'
,
this
.
commonGraphProperties
.
rect_text_width
)
.
attr
(
'
height
'
,
this
.
commonGraphProperties
.
rect_text_height
);
.
attr
({
class
:
'
rect-metric
'
,
x
:
4
,
y
:
1
,
rx
:
2
,
width
:
this
.
commonGraphProperties
.
rect_text_width
,
height
:
this
.
commonGraphProperties
.
rect_text_height
,
});
rectTextMetric
.
append
(
'
text
'
)
.
attr
(
'
class
'
,
'
text-metric
'
)
.
attr
(
'
x
'
,
currentTimeCoordinate
+
35
)
.
attr
(
'
y
'
,
maxMetricValue
+
35
)
.
attr
({
class
:
'
text-metric text-metric-bold
'
,
x
:
8
,
y
:
35
,
})
.
text
(
timeFormat
(
currentData
.
time
));
rectTextMetric
.
append
(
'
text
'
)
.
attr
(
'
class
'
,
'
text-metric-date
'
)
.
attr
(
'
x
'
,
currentTimeCoordinate
+
15
)
.
attr
(
'
y
'
,
maxMetricValue
+
15
)
.
text
(
dayFormat
(
currentData
.
time
));
.
attr
({
class
:
'
text-metric-date
'
,
x
:
8
,
y
:
15
,
})
.
text
(
dateFormat
(
currentData
.
time
));
let
currentMetricValue
=
formatRelevantDigits
(
currentData
.
value
);
if
(
key
===
'
cpu_values
'
)
{
...
...
app/assets/stylesheets/pages/environments.scss
浏览文件 @
d048d926
...
...
@@ -157,7 +157,8 @@
.prometheus-graph
{
text
{
fill
:
$stat-graph-axis-fill
;
fill
:
$gl-text-color
;
stroke-width
:
0
;
}
.label-axis-text
,
...
...
@@ -210,27 +211,33 @@
.rect-text-metric
{
fill
:
$white-light
;
stroke-width
:
1
;
stroke
:
$
black
;
stroke
:
$
gray-darkest
;
}
.rect-axis-text
{
fill
:
$white-light
;
}
.text-metric
,
.text-median-metric
,
.text-metric-usage
,
.text-metric-date
{
fill
:
$black
;
.text-metric
{
font-weight
:
600
;
}
.text-metric-date
{
font-weight
:
200
;
.selected-metric-line
{
stroke
:
$gl-gray-dark
;
stroke-width
:
1
;
}
.
selected-metric
-line
{
.
deployment
-line
{
stroke
:
$black
;
stroke-width
:
1
;
stroke-width
:
2
;
}
.deploy-info-text
{
dominant-baseline
:
text-before-edge
;
}
.text-metric-bold
{
font-weight
:
600
;
}
.prometheus-state
{
...
...
app/controllers/projects/deployments_controller.rb
0 → 100644
浏览文件 @
d048d926
class
Projects::DeploymentsController
<
Projects
::
ApplicationController
before_action
:authorize_read_environment!
before_action
:authorize_read_deployment!
def
index
deployments
=
environment
.
deployments
.
reorder
(
created_at: :desc
)
deployments
=
deployments
.
where
(
'created_at > ?'
,
params
[
:after
].
to_time
)
if
params
[
:after
]
&
.
to_time
render
json:
{
deployments:
DeploymentSerializer
.
new
(
user:
@current_user
,
project:
project
)
.
represent_concise
(
deployments
)
}
end
private
def
environment
@environment
||=
project
.
environments
.
find
(
params
[
:environment_id
])
end
end
app/serializers/deployment_entity.rb
浏览文件 @
d048d926
...
...
@@ -18,8 +18,10 @@ class DeploymentEntity < Grape::Entity
end
end
expose
:created_at
expose
:tag
expose
:last?
expose
:user
,
using:
UserEntity
expose
:commit
,
using:
CommitEntity
expose
:deployable
,
using:
BuildEntity
...
...
app/serializers/deployment_serializer.rb
0 → 100644
浏览文件 @
d048d926
class
DeploymentSerializer
<
BaseSerializer
entity
DeploymentEntity
def
represent_concise
(
resource
,
opts
=
{})
opts
[
:only
]
=
[
:iid
,
:id
,
:sha
,
:created_at
,
:tag
,
:last?
,
:id
,
ref:
[
:name
]]
represent
(
resource
,
opts
)
end
end
app/views/projects/environments/metrics.html.haml
浏览文件 @
d048d926
...
...
@@ -5,7 +5,7 @@
=
page_specific_javascript_bundle_tag
(
'monitoring'
)
=
render
"projects/pipelines/head"
.prometheus-container
{
class:
container_class
,
'data-has-metrics'
:
"#{@environment.has_metrics?}"
}
#js-metrics
.prometheus-container
{
class:
container_class
,
data:
{
has_metrics:
"#{@environment.has_metrics?}"
,
deployment_endpoint:
namespace_project_environment_deployments_path
(
@project
.
namespace
,
@project
,
@environment
,
format: :json
)
}
}
.top-area
.row
.col-sm-6
...
...
config/routes/project.rb
浏览文件 @
d048d926
...
...
@@ -138,6 +138,8 @@ constraints(ProjectUrlConstrainer.new) do
collection
do
get
:folder
,
path:
'folders/*id'
,
constraints:
{
format:
/(html|json)/
}
end
resources
:deployments
,
only:
[
:index
]
end
resource
:cycle_analytics
,
only:
[
:show
]
...
...
db/migrate/20170327091750_add_created_at_index_to_deployments.rb
0 → 100644
浏览文件 @
d048d926
class
AddCreatedAtIndexToDeployments
<
ActiveRecord
::
Migration
include
Gitlab
::
Database
::
MigrationHelpers
DOWNTIME
=
false
disable_ddl_transaction!
def
up
add_concurrent_index
:deployments
,
:created_at
end
def
down
remove_concurrent_index
:deployments
,
:created_at
end
end
db/schema.rb
浏览文件 @
d048d926
...
...
@@ -386,6 +386,7 @@ ActiveRecord::Schema.define(version: 20170426181740) do
t
.
string
"on_stop"
end
add_index
"deployments"
,
[
"created_at"
],
name:
"index_deployments_on_created_at"
,
using: :btree
add_index
"deployments"
,
[
"project_id"
,
"environment_id"
,
"iid"
],
name:
"index_deployments_on_project_id_and_environment_id_and_iid"
,
using: :btree
add_index
"deployments"
,
[
"project_id"
,
"iid"
],
name:
"index_deployments_on_project_id_and_iid"
,
unique:
true
,
using: :btree
...
...
spec/controllers/projects/deployments_controller_spec.rb
0 → 100644
浏览文件 @
d048d926
require
'spec_helper'
describe
Projects
::
DeploymentsController
do
include
ApiHelpers
let
(
:user
)
{
create
(
:user
)
}
let
(
:project
)
{
create
(
:empty_project
)
}
let
(
:environment
)
{
create
(
:environment
,
name:
'production'
,
project:
project
)
}
before
do
project
.
add_master
(
user
)
sign_in
(
user
)
end
describe
'GET #index'
do
it
'returns list of deployments from last 8 hours'
do
create
(
:deployment
,
environment:
environment
,
created_at:
9
.
hours
.
ago
)
create
(
:deployment
,
environment:
environment
,
created_at:
7
.
hours
.
ago
)
create
(
:deployment
,
environment:
environment
)
get
:index
,
environment_params
(
after:
8
.
hours
.
ago
)
expect
(
response
).
to
be_ok
expect
(
json_response
[
'deployments'
].
count
).
to
eq
(
2
)
end
it
'returns a list with deployments information'
do
create
(
:deployment
,
environment:
environment
)
get
:index
,
environment_params
expect
(
response
).
to
be_ok
expect
(
response
).
to
match_response_schema
(
'deployments'
)
end
end
def
environment_params
(
opts
=
{})
opts
.
reverse_merge
(
namespace_id:
project
.
namespace
,
project_id:
project
,
environment_id:
environment
.
id
)
end
end
spec/features/security/project/internal_access_spec.rb
浏览文件 @
d048d926
...
...
@@ -466,6 +466,21 @@ describe "Internal Project Access", feature: true do
it
{
is_expected
.
to
be_denied_for
(
:visitor
)
}
end
describe
"GET /:project_path/environments/:id/deployments"
do
let
(
:environment
)
{
create
(
:environment
,
project:
project
)
}
subject
{
namespace_project_environment_deployments_path
(
project
.
namespace
,
project
,
environment
)
}
it
{
is_expected
.
to
be_allowed_for
(
:admin
)
}
it
{
is_expected
.
to
be_allowed_for
(
:owner
).
of
(
project
)
}
it
{
is_expected
.
to
be_allowed_for
(
:master
).
of
(
project
)
}
it
{
is_expected
.
to
be_allowed_for
(
:developer
).
of
(
project
)
}
it
{
is_expected
.
to
be_allowed_for
(
:reporter
).
of
(
project
)
}
it
{
is_expected
.
to
be_denied_for
(
:guest
).
of
(
project
)
}
it
{
is_expected
.
to
be_denied_for
(
:user
)
}
it
{
is_expected
.
to
be_denied_for
(
:external
)
}
it
{
is_expected
.
to
be_denied_for
(
:visitor
)
}
end
describe
"GET /:project_path/environments/new"
do
subject
{
new_namespace_project_environment_path
(
project
.
namespace
,
project
)
}
...
...
spec/features/security/project/private_access_spec.rb
浏览文件 @
d048d926
...
...
@@ -449,6 +449,21 @@ describe "Private Project Access", feature: true do
it
{
is_expected
.
to
be_denied_for
(
:visitor
)
}
end
describe
"GET /:project_path/environments/:id/deployments"
do
let
(
:environment
)
{
create
(
:environment
,
project:
project
)
}
subject
{
namespace_project_environment_deployments_path
(
project
.
namespace
,
project
,
environment
)
}
it
{
is_expected
.
to
be_allowed_for
(
:admin
)
}
it
{
is_expected
.
to
be_allowed_for
(
:owner
).
of
(
project
)
}
it
{
is_expected
.
to
be_allowed_for
(
:master
).
of
(
project
)
}
it
{
is_expected
.
to
be_allowed_for
(
:developer
).
of
(
project
)
}
it
{
is_expected
.
to
be_allowed_for
(
:reporter
).
of
(
project
)
}
it
{
is_expected
.
to
be_denied_for
(
:guest
).
of
(
project
)
}
it
{
is_expected
.
to
be_denied_for
(
:user
)
}
it
{
is_expected
.
to
be_denied_for
(
:external
)
}
it
{
is_expected
.
to
be_denied_for
(
:visitor
)
}
end
describe
"GET /:project_path/environments/new"
do
subject
{
new_namespace_project_environment_path
(
project
.
namespace
,
project
)
}
...
...
spec/features/security/project/public_access_spec.rb
浏览文件 @
d048d926
...
...
@@ -286,6 +286,21 @@ describe "Public Project Access", feature: true do
it
{
is_expected
.
to
be_denied_for
(
:visitor
)
}
end
describe
"GET /:project_path/environments/:id/deployments"
do
let
(
:environment
)
{
create
(
:environment
,
project:
project
)
}
subject
{
namespace_project_environment_deployments_path
(
project
.
namespace
,
project
,
environment
)
}
it
{
is_expected
.
to
be_allowed_for
(
:admin
)
}
it
{
is_expected
.
to
be_allowed_for
(
:owner
).
of
(
project
)
}
it
{
is_expected
.
to
be_allowed_for
(
:master
).
of
(
project
)
}
it
{
is_expected
.
to
be_allowed_for
(
:developer
).
of
(
project
)
}
it
{
is_expected
.
to
be_allowed_for
(
:reporter
).
of
(
project
)
}
it
{
is_expected
.
to
be_denied_for
(
:guest
).
of
(
project
)
}
it
{
is_expected
.
to
be_denied_for
(
:user
)
}
it
{
is_expected
.
to
be_denied_for
(
:external
)
}
it
{
is_expected
.
to
be_denied_for
(
:visitor
)
}
end
describe
"GET /:project_path/environments/new"
do
subject
{
new_namespace_project_environment_path
(
project
.
namespace
,
project
)
}
...
...
spec/fixtures/api/schemas/deployments.json
0 → 100644
浏览文件 @
d048d926
{
"additionalProperties"
:
false
,
"properties"
:
{
"deployments"
:
{
"items"
:
{
"additionalProperties"
:
false
,
"properties"
:
{
"created_at"
:
{
"type"
:
"string"
},
"id"
:
{
"type"
:
"integer"
},
"iid"
:
{
"type"
:
"integer"
},
"last?"
:
{
"type"
:
"boolean"
},
"ref"
:
{
"additionalProperties"
:
false
,
"properties"
:
{
"name"
:
{
"type"
:
"string"
}
},
"required"
:
[
"name"
],
"type"
:
"object"
},
"sha"
:
{
"type"
:
"string"
},
"tag"
:
{
"type"
:
"boolean"
}
},
"required"
:
[
"sha"
,
"created_at"
,
"iid"
,
"tag"
,
"last?"
,
"ref"
,
"id"
],
"type"
:
"object"
},
"minItems"
:
1
,
"type"
:
"array"
}
},
"required"
:
[
"deployments"
],
"type"
:
"object"
}
spec/javascripts/fixtures/environments.rb
0 → 100644
浏览文件 @
d048d926
require
'spec_helper'
describe
Projects
::
EnvironmentsController
,
'(JavaScript fixtures)'
,
type: :controller
do
include
JavaScriptFixturesHelpers
let
(
:admin
)
{
create
(
:admin
)
}
let
(
:namespace
)
{
create
(
:namespace
,
name:
'frontend-fixtures'
)}
let
(
:project
)
{
create
(
:project_empty_repo
,
namespace:
namespace
,
path:
'environments-project'
)
}
let
(
:environment
)
{
create
(
:environment
,
name:
'production'
,
project:
project
)
}
render_views
before
(
:all
)
do
clean_frontend_fixtures
(
'environments/metrics'
)
end
before
(
:each
)
do
sign_in
(
admin
)
end
it
'environments/metrics/metrics.html.raw'
do
|
example
|
get
:metrics
,
namespace_id:
project
.
namespace
,
project_id:
project
,
id:
environment
.
id
expect
(
response
).
to
be_success
store_frontend_fixture
(
response
,
example
.
description
)
end
end
spec/javascripts/fixtures/environments/metrics.html.haml
已删除
100644 → 0
浏览文件 @
9fce7c8e
.prometheus-container
{
'data-has-metrics'
:
"false"
,
'data-doc-link'
:
'/help/administration/monitoring/prometheus/index.md'
,
'data-prometheus-integration'
:
'/root/hello-prometheus/services/prometheus/edit'
}
.top-area
.row
.col-sm-6
%h3
.page-title
Metrics for environment
.prometheus-state
.js-getting-started.hidden
.row
.col-md-4.col-md-offset-4.state-svg
%svg
.row
.col-md-6.col-md-offset-3
%h4
.text-center.state-title
Get started with performance monitoring
.row
.col-md-6.col-md-offset-3
.description-text.text-center.state-description
Stay updated about the performance and health of your environment by configuring Prometheus to monitor your deployments. Learn more about performance monitoring
.row.state-button-section
.col-md-4.col-md-offset-4.text-center.state-button
%a
.btn.btn-success
Configure Prometheus
.js-loading.hidden
.row
.col-md-4.col-md-offset-4.state-svg
%svg
.row
.col-md-6.col-md-offset-3
%h4
.text-center.state-title
Waiting for performance data
.row
.col-md-6.col-md-offset-3
.description-text.text-center.state-description
Creating graphs uses the data from the Prometheus server. If this takes a long time, ensure that data is available.
.row.state-button-section
.col-md-4.col-md-offset-4.text-center.state-button
%a
.btn.btn-success
View documentation
.js-unable-to-connect.hidden
.row
.col-md-4.col-md-offset-4.state-svg
%svg
.row
.col-md-6.col-md-offset-3
%h4
.text-center.state-title
Unable to connect to Prometheus server
.row
.col-md-6.col-md-offset-3
.description-text.text-center.state-description
Ensure connectivity is available from the GitLab server to the Prometheus server
.row.state-button-section
.col-md-4.col-md-offset-4.text-center.state-button
%a
.btn.btn-success
View documentation
.prometheus-graphs
.row
.col-sm-12
%svg
.prometheus-graph
{
'graph-type'
=>
'cpu_values'
}
.row
.col-sm-12
%svg
.prometheus-graph
{
'graph-type'
=>
'memory_values'
}
spec/javascripts/monitoring/deployments_spec.js
0 → 100644
浏览文件 @
d048d926
import
d3
from
'
d3
'
;
import
PrometheusGraph
from
'
~/monitoring/prometheus_graph
'
;
import
Deployments
from
'
~/monitoring/deployments
'
;
import
{
prometheusMockData
}
from
'
./prometheus_mock_data
'
;
describe
(
'
Metrics deployments
'
,
()
=>
{
const
fixtureName
=
'
environments/metrics/metrics.html.raw
'
;
let
deployment
;
let
prometheusGraph
;
const
graphElement
=
()
=>
document
.
querySelector
(
'
.prometheus-graph
'
);
preloadFixtures
(
fixtureName
);
beforeEach
((
done
)
=>
{
// Setup the view
loadFixtures
(
fixtureName
);
d3
.
selectAll
(
'
.prometheus-graph
'
)
.
append
(
'
g
'
)
.
attr
(
'
class
'
,
'
graph-container
'
);
prometheusGraph
=
new
PrometheusGraph
();
deployment
=
new
Deployments
(
1000
,
500
);
spyOn
(
prometheusGraph
,
'
init
'
);
spyOn
(
$
,
'
ajax
'
).
and
.
callFake
(()
=>
{
const
d
=
$
.
Deferred
();
d
.
resolve
({
deployments
:
[{
id
:
1
,
created_at
:
deployment
.
chartData
[
10
].
time
,
sha
:
'
testing
'
,
tag
:
false
,
ref
:
{
name
:
'
testing
'
,
},
},
{
id
:
2
,
created_at
:
deployment
.
chartData
[
15
].
time
,
sha
:
''
,
tag
:
true
,
ref
:
{
name
:
'
tag
'
,
},
}],
});
setTimeout
(
done
);
return
d
.
promise
();
});
prometheusGraph
.
configureGraph
();
prometheusGraph
.
transformData
(
prometheusMockData
.
metrics
);
deployment
.
init
(
prometheusGraph
.
graphSpecificProperties
.
memory_values
.
data
);
});
it
(
'
creates line on graph for deploment
'
,
()
=>
{
expect
(
graphElement
().
querySelectorAll
(
'
.deployment-line
'
).
length
,
).
toBe
(
2
);
});
it
(
'
creates hidden deploy boxes
'
,
()
=>
{
expect
(
graphElement
().
querySelectorAll
(
'
.prometheus-graph .js-deploy-info-box
'
).
length
,
).
toBe
(
2
);
});
it
(
'
hides the info boxes by default
'
,
()
=>
{
expect
(
graphElement
().
querySelectorAll
(
'
.prometheus-graph .js-deploy-info-box.hidden
'
).
length
,
).
toBe
(
2
);
});
it
(
'
shows sha short code when tag is false
'
,
()
=>
{
expect
(
graphElement
().
querySelector
(
'
.deploy-info-1-cpu_values .js-deploy-info-box
'
).
textContent
.
trim
(),
).
toContain
(
'
testin
'
);
});
it
(
'
shows ref name when tag is true
'
,
()
=>
{
expect
(
graphElement
().
querySelector
(
'
.deploy-info-2-cpu_values .js-deploy-info-box
'
).
textContent
.
trim
(),
).
toContain
(
'
tag
'
);
});
it
(
'
shows info box when moving mouse over line
'
,
()
=>
{
deployment
.
mouseOverDeployInfo
(
deployment
.
data
[
0
].
xPos
,
'
cpu_values
'
);
expect
(
graphElement
().
querySelectorAll
(
'
.prometheus-graph .js-deploy-info-box.hidden
'
).
length
,
).
toBe
(
1
);
expect
(
graphElement
().
querySelector
(
'
.deploy-info-1-cpu_values .js-deploy-info-box.hidden
'
),
).
toBeNull
();
});
it
(
'
hides previously visible info box when moving mouse away
'
,
()
=>
{
deployment
.
mouseOverDeployInfo
(
500
,
'
cpu_values
'
);
expect
(
graphElement
().
querySelectorAll
(
'
.prometheus-graph .js-deploy-info-box.hidden
'
).
length
,
).
toBe
(
2
);
expect
(
graphElement
().
querySelector
(
'
.deploy-info-1-cpu_values .js-deploy-info-box.hidden
'
),
).
not
.
toBeNull
();
});
describe
(
'
refText
'
,
()
=>
{
it
(
'
returns shortened SHA
'
,
()
=>
{
expect
(
Deployments
.
refText
({
tag
:
false
,
sha
:
'
123456789
'
,
}),
).
toBe
(
'
123456
'
);
});
it
(
'
returns tag name
'
,
()
=>
{
expect
(
Deployments
.
refText
({
tag
:
true
,
ref
:
'
v1.0
'
,
}),
).
toBe
(
'
v1.0
'
);
});
});
});
spec/javascripts/monitoring/prometheus_graph_spec.js
浏览文件 @
d048d926
...
...
@@ -3,7 +3,7 @@ import PrometheusGraph from '~/monitoring/prometheus_graph';
import
{
prometheusMockData
}
from
'
./prometheus_mock_data
'
;
describe
(
'
PrometheusGraph
'
,
()
=>
{
const
fixtureName
=
'
static/environment
s/metrics.html.raw
'
;
const
fixtureName
=
'
environments/metric
s/metrics.html.raw
'
;
const
prometheusGraphContainer
=
'
.prometheus-graph
'
;
const
prometheusGraphContents
=
`
${
prometheusGraphContainer
}
[graph-type=cpu_values]`
;
...
...
@@ -77,7 +77,7 @@ describe('PrometheusGraph', () => {
});
describe
(
'
PrometheusGraphs UX states
'
,
()
=>
{
const
fixtureName
=
'
static/environment
s/metrics.html.raw
'
;
const
fixtureName
=
'
environments/metric
s/metrics.html.raw
'
;
preloadFixtures
(
fixtureName
);
beforeEach
(()
=>
{
...
...
spec/serializers/deployment_entity_spec.rb
浏览文件 @
d048d926
...
...
@@ -3,25 +3,23 @@ require 'spec_helper'
describe
DeploymentEntity
do
let
(
:user
)
{
create
(
:user
)
}
let
(
:request
)
{
double
(
'request'
)
}
let
(
:deployment
)
{
create
(
:deployment
)
}
let
(
:entity
)
{
described_class
.
new
(
deployment
,
request:
request
)
}
subject
{
entity
.
as_json
}
before
do
allow
(
request
).
to
receive
(
:user
).
and_return
(
user
)
end
let
(
:entity
)
do
described_class
.
new
(
deployment
,
request:
request
)
end
let
(
:deployment
)
{
create
(
:deployment
)
}
subject
{
entity
.
as_json
}
it
'exposes internal deployment id'
do
expect
(
subject
).
to
include
(
:iid
)
end
it
'exposes nested information about branch'
do
expect
(
subject
[
:ref
][
:name
]).
to
eq
'master'
expect
(
subject
[
:ref
][
:ref_path
]).
not_to
be_empty
end
it
'exposes creation date'
do
expect
(
subject
).
to
include
(
:created_at
)
end
end
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录