grim/convey
Clone
Summary
Browse
Changes
Graph
Make the docker/build task call docker/export to get it's files out of the workspace
2018-02-15, Gary Kramlich
3f70720d5621
Make the docker/build task call docker/export to get it's files out of the workspace
// 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
main
import
(
"fmt"
"os"
"path/filepath"
"runtime/debug"
"strings"
"github.com/alecthomas/kingpin"
"github.com/aphistic/gomol"
"bitbucket.org/rw_grim/convey/config"
"bitbucket.org/rw_grim/convey/consts"
"bitbucket.org/rw_grim/convey/environment"
"bitbucket.org/rw_grim/convey/loaders/bitbucket"
"bitbucket.org/rw_grim/convey/loaders/codebuild"
"bitbucket.org/rw_grim/convey/loaders/convey"
"bitbucket.org/rw_grim/convey/logging"
"bitbucket.org/rw_grim/convey/runners"
"bitbucket.org/rw_grim/convey/ssh"
"bitbucket.org/rw_grim/convey/state"
)
var
(
app
=
kingpin
.
New
(
"convey"
,
"Convey is a container pipeline runner."
).
Version
(
consts
.
Version
)
color
=
app
.
Flag
(
"color"
,
"Enable colorized output"
).
Default
(
"true"
).
Bool
()
configLoader
=
app
.
Flag
(
"config-loader"
,
"Select the configuration loader"
).
Short
(
'l'
).
Default
(
"convey"
).
Enum
(
"convey"
,
"bitbucket"
,
"codebuild"
)
configFile
=
app
.
Flag
(
"config"
,
"The config file name to use"
).
Short
(
'f'
).
String
()
cpuShares
=
app
.
Flag
(
"cpu-shares"
,
"The amount of cpu shares to give to a run task"
).
Short
(
'c'
).
String
()
dockerConfig
=
app
.
Flag
(
"docker-config"
,
"Location of docker client config files"
).
String
()
env
=
app
.
Flag
(
"env"
,
"Set environment variables"
).
Short
(
'e'
).
Strings
()
forceSequential
=
app
.
Flag
(
"force-sequential"
,
"Don't run anything concurrently"
).
Short
(
'S'
).
Default
(
"False"
).
Bool
()
keep
=
app
.
Flag
(
"keep"
,
"Keep the workspace volume"
).
Short
(
'k'
).
Hidden
().
Default
(
"False"
).
Bool
()
memory
=
app
.
Flag
(
"memory"
,
"The amount of memory to give the run task"
).
Short
(
'm'
).
String
()
sshAgent
=
app
.
Flag
(
"ssh-agent"
,
"A shortcut for --ssh-identity=*"
).
Default
(
"false"
).
Bool
()
sshIdentities
=
app
.
Flag
(
"ssh-identity"
,
"Enable ssh-agent for the given identities"
).
Strings
()
planTimeout
=
app
.
Flag
(
"timeout"
,
"The maximum amount of time a plan can run. 0 to disable. Units must be specified."
).
Default
(
"15m"
).
Duration
()
verbose
=
app
.
Flag
(
"verbose"
,
"Be more verbose"
).
Short
(
'v'
).
Default
(
"False"
).
Bool
()
disableDeprecated
=
app
.
Flag
(
"disable-deprecated"
,
"Allow the use of deprecated features"
).
Default
(
"False"
).
Bool
()
options
=
app
.
Flag
(
"opt"
,
"Options to pass to the config loader"
).
Short
(
'o'
).
Strings
()
// deprecated options
graphviz
=
app
.
Flag
(
"graphviz"
,
"Output a graphviz diagram of the config file"
).
Short
(
'g'
).
Hidden
().
Default
(
"False"
).
Bool
()
listEnvironment
=
app
.
Flag
(
"list-environment"
,
"List the environment variables that are available"
).
Short
(
'E'
).
Hidden
().
Default
(
"false"
).
Bool
()
listMetaPlans
=
app
.
Flag
(
"list-meta-plans"
,
"List the meta plans that are available"
).
Short
(
'M'
).
Hidden
().
Default
(
"false"
).
Bool
()
listPlans
=
app
.
Flag
(
"list-plans"
,
"List the plans that are available"
).
Short
(
'P'
).
Hidden
().
Default
(
"false"
).
Bool
()
listTasks
=
app
.
Flag
(
"list-tasks"
,
"List the supported tasks"
).
Short
(
'L'
).
Hidden
().
Default
(
"false"
).
Bool
()
showConfig
=
app
.
Flag
(
"show-config"
,
"Show a dump of the config file"
).
Short
(
'C'
).
Hidden
().
Default
(
"false"
).
Bool
()
// simple commands
_
=
app
.
Command
(
"config"
,
"Show a dump of the config file"
)
_
=
app
.
Command
(
"environment"
,
"List the environment variables that are available"
)
_
=
app
.
Command
(
"graphviz"
,
"Output a graphviz diagram of the config file"
)
// run command
run
=
app
.
Command
(
"run"
,
"Run a plan or metaplan"
).
Default
()
planNames
=
run
.
Arg
(
"plan"
,
"The plan or list of plans to run in specified order"
).
Default
(
"default"
).
Strings
()
// list commands
ls
=
app
.
Command
(
"list"
,
"List information"
).
Alias
(
"ls"
)
_
=
ls
.
Command
(
"environment"
,
"List the environment variables that are available."
).
Alias
(
"env"
)
_
=
ls
.
Command
(
"metaplans"
,
"List the metaplans defined in the configuration file."
)
_
=
ls
.
Command
(
"plans"
,
"List the plans defined in the configuration file."
)
_
=
ls
.
Command
(
"tasks"
,
"List the tasks defined in the configuration file."
)
)
// determineLoader will return the config loader to use.
func
determineLoader
()
config
.
Loader
{
switch
*
configLoader
{
case
"bitbucket"
:
return
&
bitbucket
.
Loader
{}
case
"codebuild"
:
return
&
codebuild
.
Loader
{}
default
:
return
&
convey
.
Loader
{}
}
}
// determineRunner will return the runner that handles the given command.
func
determineRunner
(
command
string
)
runners
.
Runner
{
switch
command
{
case
"graphviz"
:
return
&
runners
.
Graphviz
{}
case
"list environment"
:
return
&
runners
.
ListEnvironment
{}
case
"list metaplans"
:
return
&
runners
.
ListMetaPlans
{}
case
"list plans"
:
return
&
runners
.
ListPlans
{}
case
"list tasks"
:
return
&
runners
.
ListTasks
{}
case
"config"
:
return
&
runners
.
ShowConfig
{}
default
:
return
&
runners
.
Convey
{}
}
}
// handleDeprecatedCommands will check deprecated command line flags and return
// the current command that they should run.
func
handleDeprecatedCommands
()
string
{
if
*
disableDeprecated
{
return
""
}
command
:=
""
if
*
graphviz
{
fmt
.
Printf
(
"--graphviz is deprecated, use 'convey graphviz` instead"
)
command
=
"graphviz"
}
else
if
*
listEnvironment
{
fmt
.
Printf
(
"--list-environment is deprecated, use 'convey list environment` instead"
)
command
=
"list environment"
}
else
if
*
listMetaPlans
{
fmt
.
Printf
(
"--list-metaplans is deprecated, use `convey list metaplans` instead"
)
command
=
"list metaplans"
}
else
if
*
listPlans
{
fmt
.
Printf
(
"--list-plans is deprecated, use `convey list plans` instead"
)
command
=
"list plans"
}
else
if
*
listTasks
{
fmt
.
Printf
(
"--list-tasks is deprecated, use `convey list tasks` instead"
)
command
=
"list tasks"
}
else
if
*
showConfig
{
fmt
.
Printf
(
"--list-plans is deprecated, use `convey config` instead"
)
command
=
"config"
}
return
command
}
// resolvePlans will run through all of the plans in the config and resolve
// their names via the loader.
func
resolvePlans
(
cfg
*
config
.
Config
,
loader
config
.
Loader
,
st
*
state
.
State
)
[]
string
{
realPlans
:=
[]
string
{}
for
_
,
planName
:=
range
*
planNames
{
if
metaPlan
,
found
:=
cfg
.
MetaPlans
[
planName
];
found
{
realPlans
=
append
(
realPlans
,
metaPlan
.
Plans
...
)
}
else
{
realPlans
=
append
(
realPlans
,
loader
.
ResolvePlanName
(
planName
,
cfg
,
st
))
}
}
return
realPlans
}
// loadConfig will load the config and it's path or an error.
func
loadConfig
(
loader
config
.
Loader
)
(
*
config
.
Config
,
string
,
error
)
{
// if a config file was not provided search for the loader's default file
if
*
configFile
==
""
{
for
_
,
filename
:=
range
loader
.
Filenames
()
{
if
_
,
err
:=
os
.
Stat
(
filename
);
os
.
IsNotExist
(
err
)
{
continue
}
*
configFile
=
filename
break
}
}
// now make sure we found a config file
if
*
configFile
==
""
{
err
:=
fmt
.
Errorf
(
"config file not found, looking for %s\n"
,
strings
.
Join
(
loader
.
Filenames
(),
","
))
return
nil
,
""
,
err
}
// figure out the path to the config file
cfgPath
,
err
:=
filepath
.
Abs
(
filepath
.
Dir
(
*
configFile
))
if
err
!=
nil
{
return
nil
,
""
,
err
}
cfg
,
err
:=
config
.
LoadFile
(
*
configFile
,
loader
,
*
options
,
*
disableDeprecated
)
if
err
!=
nil
{
return
nil
,
""
,
err
}
return
cfg
,
cfgPath
,
nil
}
func
gomain
()
int
{
args
:=
os
.
Args
[
1
:]
command
,
err
:=
app
.
Parse
(
args
)
if
err
!=
nil
{
app
.
Usage
(
args
)
return
1
}
// create our state
st
:=
state
.
New
()
// setup logging
if
err
:=
logging
.
Setup
(
*
color
,
*
verbose
);
err
!=
nil
{
fmt
.
Printf
(
"failed to setup logging: %s\n"
,
err
)
return
1
}
defer
gomol
.
ShutdownLoggers
()
// now defer the state shutdown so we can still log
defer
st
.
Destroy
()
// now load the config
loader
:=
determineLoader
()
cfg
,
cfgPath
,
err
:=
loadConfig
(
loader
)
if
err
!=
nil
{
fmt
.
Printf
(
"%s\n"
,
err
)
return
1
}
// find our default environment variables and then update them with the
// values from the command line
defEnv
,
err
:=
environment
.
Initialize
(
cfgPath
)
if
err
!=
nil
{
fmt
.
Printf
(
"%s\n"
,
err
)
return
1
}
// if the user specified the shortcut, add * to the list of acceptable keys
if
*
sshAgent
{
*
sshIdentities
=
append
(
*
sshIdentities
,
"*"
)
}
// now merge in the keys from the config
*
sshIdentities
=
append
(
*
sshIdentities
,
cfg
.
SSHIdentities
...
)
// now check if we have any keys and make sure one of them is usable
enableSSHAgent
,
err
:=
ssh
.
ShouldEnable
(
*
sshIdentities
)
if
err
!=
nil
{
fmt
.
Printf
(
"%s\n"
,
err
)
return
1
}
// set the state's variables and validate it
st
.
CfgPath
=
cfgPath
st
.
KeepWorkspace
=
*
keep
st
.
DisableDeprecated
=
*
disableDeprecated
st
.
ForceSequential
=
*
forceSequential
st
.
EnableSSHAgent
=
enableSSHAgent
st
.
PlanTimeout
=
*
planTimeout
st
.
DockerConfig
=
*
dockerConfig
st
.
CPUShares
=
*
cpuShares
st
.
Memory
=
*
memory
st
.
MergeEnv
(
environment
.
Merge
(
defEnv
,
*
env
))
if
err
:=
st
.
Valid
();
err
!=
nil
{
fmt
.
Printf
(
"%s\n"
,
err
)
return
1
}
if
commandOverride
:=
handleDeprecatedCommands
();
commandOverride
!=
""
{
command
=
commandOverride
}
runner
:=
determineRunner
(
command
)
if
len
(
*
planNames
)
==
0
{
*
planNames
=
[]
string
{
loader
.
DefaultPlan
()}
}
// resolve the plan name with the load and options
realPlans
:=
resolvePlans
(
cfg
,
loader
,
st
)
return
runner
.
Run
(
cfg
,
realPlans
,
*
env
,
st
)
}
func
main
()
{
exitCode
:=
0
defer
func
()
{
if
r
:=
recover
();
r
!=
nil
{
fmt
.
Printf
(
"panic: %s\n%s"
,
r
,
debug
.
Stack
())
}
os
.
Exit
(
exitCode
)
}()
exitCode
=
gomain
()
}