grim/convey
Clone
Summary
Browse
Changes
Graph
Bump the version for release
v0.14.0-alpha3
2018-02-20, Gary Kramlich
166a6d1979fa
Bump the version for release
// Convey
// Copyright 2016-2018 Gary Kramlich <grim@reaperworld.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// Package stages contains the stages api.
package
stages
import
(
"fmt"
"strings"
"sync"
"github.com/aphistic/gomol"
"bitbucket.org/rw_grim/convey/environment"
"bitbucket.org/rw_grim/convey/logging"
"bitbucket.org/rw_grim/convey/state"
"bitbucket.org/rw_grim/convey/tasks"
"bitbucket.org/rw_grim/convey/yaml"
)
const
(
runAlways
=
"always"
runOnFailure
=
"on-failure"
runOnSuccess
=
"on-success"
)
// Stage is a representation of a stage.
type
Stage
struct
{
Name
string
`yaml:"name"`
Run
string
`yaml:"run"`
Environment
yaml
.
StringOrSlice
`yaml:"environment"`
Tasks
yaml
.
StringOrSlice
`yaml:"tasks"`
Enabled
bool
`yaml:"enabled"`
Always
bool
`yaml:"always"`
Concurrent
bool
`yaml:"concurrent"`
}
// UnmarshalYAML is a custom yaml unmarshaller for stages.
func
(
s
*
Stage
)
UnmarshalYAML
(
unmarshal
func
(
interface
{})
error
)
error
{
type
rawStage
Stage
raw
:=
rawStage
{
Enabled
:
true
}
if
err
:=
unmarshal
(
&
raw
);
err
!=
nil
{
return
err
}
*
s
=
Stage
(
raw
)
// validate the run attribute
if
s
.
Run
==
""
{
// If the always attribute is set, convert it to run syntax
if
s
.
Always
{
s
.
Run
=
runAlways
}
else
{
s
.
Run
=
runOnSuccess
}
}
else
{
s
.
Run
=
strings
.
ToLower
(
s
.
Run
)
}
// now validate the run attribute
switch
s
.
Run
{
case
runOnSuccess
:
case
runOnFailure
:
case
runAlways
:
default
:
return
fmt
.
Errorf
(
"Invalid value '%s' for run attribute"
,
s
.
Run
)
}
// All good, return success
return
nil
}
// Execute runs the stage.
func
(
s
*
Stage
)
Execute
(
path
string
,
logger
*
gomol
.
LogAdapter
,
taskMap
map
[
string
]
tasks
.
Task
,
env
[]
string
,
st
*
state
.
State
)
error
{
stageEnv
:=
environment
.
Merge
(
env
,
s
.
Environment
)
if
s
.
Concurrent
&&
!
st
.
ForceSequential
{
taskRes
:=
make
(
chan
error
)
stageRes
:=
make
(
chan
error
)
wg
:=
sync
.
WaitGroup
{}
// process all of the task results
go
func
(
taskRes
,
stageRes
chan
error
)
{
var
stageErr
error
for
err
:=
range
taskRes
{
if
err
!=
nil
&&
stageErr
==
nil
{
stageErr
=
err
}
}
stageRes
<-
stageErr
// now close the stageRes channel
close
(
stageRes
)
}(
taskRes
,
stageRes
)
// run all of the tasks in go routines
for
_
,
taskName
:=
range
s
.
Tasks
{
wg
.
Add
(
1
)
go
func
(
path
,
name
string
,
res
chan
error
)
{
defer
wg
.
Done
()
res
<-
s
.
runTask
(
path
,
name
,
stageEnv
,
taskMap
,
st
)
}(
path
,
taskName
,
taskRes
)
}
// block until the task wait group is done, then close the taskRes channel
wg
.
Wait
()
close
(
taskRes
)
// now return the result from the stageRes channel
return
<-
stageRes
}
// serial execution
for
_
,
taskName
:=
range
s
.
Tasks
{
err
:=
s
.
runTask
(
path
,
taskName
,
stageEnv
,
taskMap
,
st
)
if
err
!=
nil
{
return
err
}
}
return
nil
}
func
(
s
*
Stage
)
runTask
(
path
,
name
string
,
stageEnv
[]
string
,
taskMap
map
[
string
]
tasks
.
Task
,
st
*
state
.
State
)
error
{
absTaskName
:=
fmt
.
Sprintf
(
"%s/%s"
,
path
,
name
)
taskLogger
:=
logging
.
NewAdapter
(
fmt
.
Sprintf
(
"%s/%s"
,
path
,
name
))
if
lErr
:=
taskLogger
.
Info
(
"starting"
);
lErr
!=
nil
{
fmt
.
Printf
(
"error reporting info: %s\n"
,
lErr
)
}
task
,
found
:=
taskMap
[
name
]
if
!
found
{
if
lErr
:=
taskLogger
.
Fatal
(
"failed, task not found"
);
lErr
!=
nil
{
fmt
.
Printf
(
"error reporting fatal: %s\n"
,
lErr
)
}
return
fmt
.
Errorf
(
"task %s not found"
,
absTaskName
)
}
err
:=
task
.
Execute
(
absTaskName
,
taskLogger
,
stageEnv
,
st
)
if
err
!=
nil
{
if
lErr
:=
taskLogger
.
Fatalf
(
"failed, %s"
,
err
);
lErr
!=
nil
{
fmt
.
Printf
(
"error reporting fatal: %s\n"
,
lErr
)
}
return
err
}
if
lErr
:=
taskLogger
.
Info
(
"finished"
);
lErr
!=
nil
{
fmt
.
Printf
(
"error reporting info: %s\n"
,
lErr
)
}
return
nil
}
// ShouldRun will return True if a stage should be run based on the passed in
// error (from the plan) and whether or not the stage is enabled.
func
(
s
*
Stage
)
ShouldRun
(
err
error
)
bool
{
if
!
s
.
Enabled
{
return
false
}
if
s
.
Run
==
runAlways
{
return
true
}
if
err
!=
nil
{
return
s
.
Run
==
runOnFailure
}
return
s
.
Run
==
runOnSuccess
}