runservice: update docker registry auth
This commit is contained in:
parent
d91bb36ccb
commit
dfeba334f6
1
go.mod
1
go.mod
|
@ -19,6 +19,7 @@ require (
|
|||
github.com/go-ini/ini v1.42.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.4.1 // indirect
|
||||
github.com/google/go-cmp v0.3.0
|
||||
github.com/google/go-containerregistry v0.0.0-20190412005658-1d38b9cfdb9d
|
||||
github.com/google/go-jsonnet v0.12.1
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect
|
||||
github.com/gorilla/handlers v1.4.0
|
||||
|
|
4
go.sum
4
go.sum
|
@ -62,10 +62,10 @@ github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a h1:ZJu5NB1Bk5ms4vw0Xu
|
|||
github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a h1:ZJu5NB1Bk5ms4vw0Xu4i+jD32SE9jQXyfnOvwhHqlT0=
|
||||
github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-containerregistry v0.0.0-20190412005658-1d38b9cfdb9d h1:K8AF5hFHsOYRk0CG22FwQk3oCu7CbL2bNfiHoaGuW4Y=
|
||||
github.com/google/go-containerregistry v0.0.0-20190412005658-1d38b9cfdb9d/go.mod h1:yZAFP63pRshzrEYLXLGPmUt0Ay+2zdjmMN1loCnRLUk=
|
||||
github.com/google/go-jsonnet v0.12.1 h1:v0iUm/b4SBz7lR/diMoz9tLAz8lqtnNRKIwMrmU2HEU=
|
||||
github.com/google/go-jsonnet v0.12.1/go.mod h1:gVu3UVSfOt5fRFq+dh9duBqXa5905QY8S1QvMNcEIVs=
|
||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||
|
|
|
@ -59,30 +59,34 @@ const (
|
|||
RuntimeTypePod RuntimeType = "pod"
|
||||
)
|
||||
|
||||
type RegistryAuthType string
|
||||
type DockerRegistryAuthType string
|
||||
|
||||
const (
|
||||
RegistryAuthTypeDefault RegistryAuthType = "default"
|
||||
DockerRegistryAuthTypeBasic DockerRegistryAuthType = "basic"
|
||||
DockerRegistryAuthTypeEncodedAuth DockerRegistryAuthType = "encodedauth"
|
||||
)
|
||||
|
||||
type RegistryAuth struct {
|
||||
Type RegistryAuthType `json:"type"`
|
||||
type DockerRegistryAuth struct {
|
||||
Type DockerRegistryAuthType `json:"type"`
|
||||
|
||||
// default auth
|
||||
// basic auth
|
||||
Username Value `json:"username"`
|
||||
Password Value `json:"password"`
|
||||
|
||||
// encoded auth string
|
||||
Auth string `json:"auth"`
|
||||
|
||||
// future auths like aws ecr auth
|
||||
}
|
||||
|
||||
type Runtime struct {
|
||||
Type RuntimeType `json:"type,omitempty"`
|
||||
Auth *RegistryAuth `json:"auth"`
|
||||
Arch common.Arch `json:"arch,omitempty"`
|
||||
Containers []*Container `json:"containers,omitempty"`
|
||||
Type RuntimeType `json:"type,omitempty"`
|
||||
Arch common.Arch `json:"arch,omitempty"`
|
||||
Containers []*Container `json:"containers,omitempty"`
|
||||
}
|
||||
|
||||
type Container struct {
|
||||
Image string `json:"image,omitempty"`
|
||||
Auth *RegistryAuth `json:"auth"`
|
||||
Environment map[string]Value `json:"environment,omitempty"`
|
||||
User string `json:"user"`
|
||||
Privileged bool `json:"privileged"`
|
||||
|
@ -90,22 +94,24 @@ type Container struct {
|
|||
}
|
||||
|
||||
type Run struct {
|
||||
Name string `json:"name"`
|
||||
Tasks []*Task `json:"tasks"`
|
||||
Name string `json:"name"`
|
||||
Tasks []*Task `json:"tasks"`
|
||||
DockerRegistriesAuth map[string]*DockerRegistryAuth `json:"docker_registries_auth"`
|
||||
}
|
||||
|
||||
type Task struct {
|
||||
Name string `json:"name"`
|
||||
Runtime *Runtime `json:"runtime"`
|
||||
Environment map[string]Value `json:"environment,omitempty"`
|
||||
WorkingDir string `json:"working_dir"`
|
||||
Shell string `json:"shell"`
|
||||
User string `json:"user"`
|
||||
Steps []interface{} `json:"steps"`
|
||||
Depends []*Depend `json:"depends"`
|
||||
IgnoreFailure bool `json:"ignore_failure"`
|
||||
Approval bool `json:"approval"`
|
||||
When *types.When `json:"when"`
|
||||
Name string `json:"name"`
|
||||
Runtime *Runtime `json:"runtime"`
|
||||
Environment map[string]Value `json:"environment,omitempty"`
|
||||
WorkingDir string `json:"working_dir"`
|
||||
Shell string `json:"shell"`
|
||||
User string `json:"user"`
|
||||
Steps []interface{} `json:"steps"`
|
||||
Depends []*Depend `json:"depends"`
|
||||
IgnoreFailure bool `json:"ignore_failure"`
|
||||
Approval bool `json:"approval"`
|
||||
When *types.When `json:"when"`
|
||||
DockerRegistriesAuth map[string]*DockerRegistryAuth `json:"docker_registries_auth"`
|
||||
}
|
||||
|
||||
type DependCondition string
|
||||
|
@ -187,17 +193,18 @@ func (t *Task) UnmarshalJSON(b []byte) error {
|
|||
}
|
||||
|
||||
type runtask struct {
|
||||
Name string `json:"name"`
|
||||
Runtime *Runtime `json:"runtime"`
|
||||
Environment map[string]Value `json:"environment,omitempty"`
|
||||
WorkingDir string `json:"working_dir"`
|
||||
Shell string `json:"shell"`
|
||||
User string `json:"user"`
|
||||
Steps []map[string]interface{} `json:"steps"`
|
||||
Depends []interface{} `json:"depends"`
|
||||
IgnoreFailure bool `json:"ignore_failure"`
|
||||
Approval bool `json:"approval"`
|
||||
When *when `json:"when"`
|
||||
Name string `json:"name"`
|
||||
Runtime *Runtime `json:"runtime"`
|
||||
Environment map[string]Value `json:"environment,omitempty"`
|
||||
WorkingDir string `json:"working_dir"`
|
||||
Shell string `json:"shell"`
|
||||
User string `json:"user"`
|
||||
Steps []map[string]interface{} `json:"steps"`
|
||||
Depends []interface{} `json:"depends"`
|
||||
IgnoreFailure bool `json:"ignore_failure"`
|
||||
Approval bool `json:"approval"`
|
||||
When *when `json:"when"`
|
||||
DockerRegistriesAuth map[string]*DockerRegistryAuth `json:"docker_registries_auth"`
|
||||
}
|
||||
|
||||
var tr *runtask
|
||||
|
@ -214,6 +221,7 @@ func (t *Task) UnmarshalJSON(b []byte) error {
|
|||
t.User = tr.User
|
||||
t.IgnoreFailure = tr.IgnoreFailure
|
||||
t.Approval = tr.Approval
|
||||
t.DockerRegistriesAuth = tr.DockerRegistriesAuth
|
||||
|
||||
steps := make([]interface{}, len(tr.Steps))
|
||||
for i, stepEntry := range tr.Steps {
|
||||
|
@ -766,27 +774,25 @@ func checkConfig(config *Config) error {
|
|||
|
||||
// Set defaults
|
||||
for _, run := range config.Runs {
|
||||
// set auth type to basic if not specified
|
||||
for _, registryAuth := range run.DockerRegistriesAuth {
|
||||
if registryAuth.Type == "" {
|
||||
registryAuth.Type = DockerRegistryAuthTypeBasic
|
||||
}
|
||||
}
|
||||
for _, task := range run.Tasks {
|
||||
// set auth type to basic if not specified
|
||||
for _, registryAuth := range task.DockerRegistriesAuth {
|
||||
if registryAuth.Type == "" {
|
||||
registryAuth.Type = DockerRegistryAuthTypeBasic
|
||||
}
|
||||
}
|
||||
|
||||
// set task default working dir
|
||||
if task.WorkingDir == "" {
|
||||
task.WorkingDir = defaultWorkingDir
|
||||
}
|
||||
|
||||
// set auth type to default if not specified
|
||||
runtime := task.Runtime
|
||||
if runtime.Auth != nil {
|
||||
if runtime.Auth.Type == "" {
|
||||
runtime.Auth.Type = RegistryAuthTypeDefault
|
||||
}
|
||||
}
|
||||
for _, container := range runtime.Containers {
|
||||
if container.Auth != nil {
|
||||
if container.Auth.Type == "" {
|
||||
container.Auth.Type = RegistryAuthTypeDefault
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set steps defaults
|
||||
for i, s := range task.Steps {
|
||||
switch step := s.(type) {
|
||||
|
|
|
@ -170,14 +170,20 @@ func TestParseOutput(t *testing.T) {
|
|||
in: `
|
||||
runs:
|
||||
- name: run01
|
||||
docker_registries_auth:
|
||||
index.docker.io:
|
||||
username: username
|
||||
password:
|
||||
from_variable: password
|
||||
tasks:
|
||||
- name: task01
|
||||
runtime:
|
||||
type: pod
|
||||
auth:
|
||||
docker_registries_auth:
|
||||
index.docker.io:
|
||||
username: username
|
||||
password:
|
||||
from_variable: password
|
||||
runtime:
|
||||
type: pod
|
||||
containers:
|
||||
- image: image01
|
||||
auth:
|
||||
|
@ -263,25 +269,29 @@ func TestParseOutput(t *testing.T) {
|
|||
Runs: []*Run{
|
||||
&Run{
|
||||
Name: "run01",
|
||||
DockerRegistriesAuth: map[string]*DockerRegistryAuth{
|
||||
"index.docker.io": {
|
||||
Type: DockerRegistryAuthTypeBasic,
|
||||
Username: Value{Type: ValueTypeString, Value: "username"},
|
||||
Password: Value{Type: ValueTypeFromVariable, Value: "password"},
|
||||
},
|
||||
},
|
||||
Tasks: []*Task{
|
||||
&Task{
|
||||
Name: "task01",
|
||||
Runtime: &Runtime{
|
||||
Type: "pod",
|
||||
Auth: &RegistryAuth{
|
||||
Type: RegistryAuthTypeDefault,
|
||||
DockerRegistriesAuth: map[string]*DockerRegistryAuth{
|
||||
"index.docker.io": {
|
||||
Type: DockerRegistryAuthTypeBasic,
|
||||
Username: Value{Type: ValueTypeString, Value: "username"},
|
||||
Password: Value{Type: ValueTypeFromVariable, Value: "password"},
|
||||
},
|
||||
},
|
||||
Runtime: &Runtime{
|
||||
Type: "pod",
|
||||
Arch: "",
|
||||
Containers: []*Container{
|
||||
&Container{
|
||||
Image: "image01",
|
||||
Auth: &RegistryAuth{
|
||||
Type: RegistryAuthTypeDefault,
|
||||
Username: Value{Type: ValueTypeFromVariable, Value: "username2"},
|
||||
Password: Value{Type: ValueTypeString, Value: "password2"},
|
||||
},
|
||||
Environment: map[string]Value{
|
||||
"ENV01": Value{Type: ValueTypeString, Value: "ENV01"},
|
||||
"ENVFROMVARIABLE01": Value{Type: ValueTypeFromVariable, Value: "variable01"},
|
||||
|
|
|
@ -37,23 +37,6 @@ func genRuntime(c *config.Config, ce *config.Runtime, variables map[string]strin
|
|||
Entrypoint: cc.Entrypoint,
|
||||
}
|
||||
|
||||
// Set container auth
|
||||
if cc.Auth != nil {
|
||||
container.Auth = &rstypes.RegistryAuth{
|
||||
Type: rstypes.RegistryAuthType(cc.Auth.Type),
|
||||
Username: genValue(cc.Auth.Username, variables),
|
||||
Password: genValue(cc.Auth.Password, variables),
|
||||
}
|
||||
}
|
||||
// if container auth is nil use runtime auth
|
||||
if container.Auth == nil && ce.Auth != nil {
|
||||
container.Auth = &rstypes.RegistryAuth{
|
||||
Type: rstypes.RegistryAuthType(ce.Auth.Type),
|
||||
Username: genValue(ce.Auth.Username, variables),
|
||||
Password: genValue(ce.Auth.Password, variables),
|
||||
}
|
||||
}
|
||||
|
||||
containers = append(containers, container)
|
||||
}
|
||||
|
||||
|
@ -199,17 +182,39 @@ func GenRunConfigTasks(uuid util.UUIDGenerator, c *config.Config, runName string
|
|||
tEnv := genEnv(ct.Environment, variables)
|
||||
|
||||
t := &rstypes.RunConfigTask{
|
||||
ID: uuid.New(ct.Name).String(),
|
||||
Name: ct.Name,
|
||||
Runtime: genRuntime(c, ct.Runtime, variables),
|
||||
Environment: tEnv,
|
||||
WorkingDir: ct.WorkingDir,
|
||||
Shell: ct.Shell,
|
||||
User: ct.User,
|
||||
Steps: steps,
|
||||
IgnoreFailure: ct.IgnoreFailure,
|
||||
Skip: !include,
|
||||
NeedsApproval: ct.Approval,
|
||||
ID: uuid.New(ct.Name).String(),
|
||||
Name: ct.Name,
|
||||
Runtime: genRuntime(c, ct.Runtime, variables),
|
||||
Environment: tEnv,
|
||||
WorkingDir: ct.WorkingDir,
|
||||
Shell: ct.Shell,
|
||||
User: ct.User,
|
||||
Steps: steps,
|
||||
IgnoreFailure: ct.IgnoreFailure,
|
||||
Skip: !include,
|
||||
NeedsApproval: ct.Approval,
|
||||
DockerRegistriesAuth: make(map[string]rstypes.DockerRegistryAuth),
|
||||
}
|
||||
|
||||
if cr.DockerRegistriesAuth != nil {
|
||||
for regname, auth := range cr.DockerRegistriesAuth {
|
||||
t.DockerRegistriesAuth[regname] = rstypes.DockerRegistryAuth{
|
||||
Type: rstypes.DockerRegistryAuthType(auth.Type),
|
||||
Username: genValue(auth.Username, variables),
|
||||
Password: genValue(auth.Password, variables),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// override with per task docker registry auth
|
||||
if ct.DockerRegistriesAuth != nil {
|
||||
for regname, auth := range ct.DockerRegistriesAuth {
|
||||
t.DockerRegistriesAuth[regname] = rstypes.DockerRegistryAuth{
|
||||
Type: rstypes.DockerRegistryAuthType(auth.Type),
|
||||
Username: genValue(auth.Username, variables),
|
||||
Password: genValue(auth.Password, variables),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rcts[t.ID] = t
|
||||
|
|
|
@ -635,25 +635,29 @@ func TestGenRunConfig(t *testing.T) {
|
|||
Runs: []*config.Run{
|
||||
&config.Run{
|
||||
Name: "run01",
|
||||
DockerRegistriesAuth: map[string]*config.DockerRegistryAuth{
|
||||
"index.docker.io": {
|
||||
Type: config.DockerRegistryAuthTypeBasic,
|
||||
Username: config.Value{Type: config.ValueTypeString, Value: "username"},
|
||||
Password: config.Value{Type: config.ValueTypeFromVariable, Value: "password"},
|
||||
},
|
||||
},
|
||||
Tasks: []*config.Task{
|
||||
&config.Task{
|
||||
Name: "task01",
|
||||
DockerRegistriesAuth: map[string]*config.DockerRegistryAuth{
|
||||
"index.docker.io": {
|
||||
Type: config.DockerRegistryAuthTypeBasic,
|
||||
Username: config.Value{Type: config.ValueTypeFromVariable, Value: "registry_username"},
|
||||
Password: config.Value{Type: config.ValueTypeString, Value: "password2"},
|
||||
},
|
||||
},
|
||||
Runtime: &config.Runtime{
|
||||
Type: "pod",
|
||||
Auth: &config.RegistryAuth{
|
||||
Type: config.RegistryAuthTypeDefault,
|
||||
Username: config.Value{Type: config.ValueTypeString, Value: "username"},
|
||||
Password: config.Value{Type: config.ValueTypeFromVariable, Value: "password"},
|
||||
},
|
||||
Arch: "",
|
||||
Containers: []*config.Container{
|
||||
&config.Container{
|
||||
Image: "image01",
|
||||
Auth: &config.RegistryAuth{
|
||||
Type: config.RegistryAuthTypeDefault,
|
||||
Username: config.Value{Type: config.ValueTypeFromVariable, Value: "registry_username"},
|
||||
Password: config.Value{Type: config.ValueTypeString, Value: "password2"},
|
||||
},
|
||||
Environment: map[string]config.Value{
|
||||
"ENV01": config.Value{Type: config.ValueTypeString, Value: "ENV01"},
|
||||
"ENVFROMVARIABLE01": config.Value{Type: config.ValueTypeFromVariable, Value: "variable01"},
|
||||
|
@ -721,15 +725,17 @@ func TestGenRunConfig(t *testing.T) {
|
|||
uuid.New("task01").String(): &rstypes.RunConfigTask{
|
||||
ID: uuid.New("task01").String(),
|
||||
Name: "task01", Depends: map[string]*rstypes.RunConfigTaskDepend{},
|
||||
DockerRegistriesAuth: map[string]rstypes.DockerRegistryAuth{
|
||||
"index.docker.io": {
|
||||
Type: rstypes.DockerRegistryAuthTypeBasic,
|
||||
Username: "yourregistryusername",
|
||||
Password: "password2",
|
||||
},
|
||||
},
|
||||
Runtime: &rstypes.Runtime{Type: rstypes.RuntimeType("pod"),
|
||||
Containers: []*rstypes.Container{
|
||||
{
|
||||
Image: "image01",
|
||||
Auth: &rstypes.RegistryAuth{
|
||||
Type: rstypes.RegistryAuthTypeDefault,
|
||||
Username: "yourregistryusername",
|
||||
Password: "password2",
|
||||
},
|
||||
Environment: map[string]string{
|
||||
"ENV01": "ENV01",
|
||||
"ENVFROMVARIABLE01": "VARVALUE01",
|
||||
|
@ -751,21 +757,23 @@ func TestGenRunConfig(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
name: "test runtime auth used for container nil auth",
|
||||
name: "test run auth used for task undefined auth",
|
||||
in: &config.Config{
|
||||
Runs: []*config.Run{
|
||||
&config.Run{
|
||||
Name: "run01",
|
||||
DockerRegistriesAuth: map[string]*config.DockerRegistryAuth{
|
||||
"index.docker.io": {
|
||||
Type: config.DockerRegistryAuthTypeBasic,
|
||||
Username: config.Value{Type: config.ValueTypeString, Value: "username"},
|
||||
Password: config.Value{Type: config.ValueTypeFromVariable, Value: "password"},
|
||||
},
|
||||
},
|
||||
Tasks: []*config.Task{
|
||||
&config.Task{
|
||||
Name: "task01",
|
||||
Runtime: &config.Runtime{
|
||||
Type: "pod",
|
||||
Auth: &config.RegistryAuth{
|
||||
Type: config.RegistryAuthTypeDefault,
|
||||
Username: config.Value{Type: config.ValueTypeString, Value: "username"},
|
||||
Password: config.Value{Type: config.ValueTypeFromVariable, Value: "password"},
|
||||
},
|
||||
Arch: "",
|
||||
Containers: []*config.Container{
|
||||
&config.Container{
|
||||
|
@ -795,15 +803,17 @@ func TestGenRunConfig(t *testing.T) {
|
|||
uuid.New("task01").String(): &rstypes.RunConfigTask{
|
||||
ID: uuid.New("task01").String(),
|
||||
Name: "task01", Depends: map[string]*rstypes.RunConfigTaskDepend{},
|
||||
DockerRegistriesAuth: map[string]rstypes.DockerRegistryAuth{
|
||||
"index.docker.io": {
|
||||
Type: rstypes.DockerRegistryAuthTypeBasic,
|
||||
Username: "username",
|
||||
Password: "yourregistrypassword",
|
||||
},
|
||||
},
|
||||
Runtime: &rstypes.Runtime{Type: rstypes.RuntimeType("pod"),
|
||||
Containers: []*rstypes.Container{
|
||||
{
|
||||
Image: "image01",
|
||||
Auth: &rstypes.RegistryAuth{
|
||||
Type: rstypes.RegistryAuthTypeDefault,
|
||||
Username: "username",
|
||||
Password: "yourregistrypassword",
|
||||
},
|
||||
Image: "image01",
|
||||
Environment: map[string]string{},
|
||||
},
|
||||
},
|
||||
|
@ -816,30 +826,44 @@ func TestGenRunConfig(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
name: "test runtime auth not used for container with auth",
|
||||
name: "test run auth override by task auth",
|
||||
in: &config.Config{
|
||||
Runs: []*config.Run{
|
||||
&config.Run{
|
||||
Name: "run01",
|
||||
DockerRegistriesAuth: map[string]*config.DockerRegistryAuth{
|
||||
"index.docker.io": {
|
||||
Type: config.DockerRegistryAuthTypeBasic,
|
||||
Username: config.Value{Type: config.ValueTypeString, Value: "username"},
|
||||
Password: config.Value{Type: config.ValueTypeFromVariable, Value: "password"},
|
||||
},
|
||||
"https://myregistry.example.com": {
|
||||
Type: config.DockerRegistryAuthTypeBasic,
|
||||
Username: config.Value{Type: config.ValueTypeString, Value: "username"},
|
||||
Password: config.Value{Type: config.ValueTypeFromVariable, Value: "password"},
|
||||
},
|
||||
},
|
||||
Tasks: []*config.Task{
|
||||
&config.Task{
|
||||
Name: "task01",
|
||||
DockerRegistriesAuth: map[string]*config.DockerRegistryAuth{
|
||||
"index.docker.io": {
|
||||
Type: config.DockerRegistryAuthTypeBasic,
|
||||
Username: config.Value{Type: config.ValueTypeFromVariable, Value: "registry_username"},
|
||||
Password: config.Value{Type: config.ValueTypeString, Value: "password2"},
|
||||
},
|
||||
"https://anotherregistry.example.com": {
|
||||
Type: config.DockerRegistryAuthTypeBasic,
|
||||
Username: config.Value{Type: config.ValueTypeFromVariable, Value: "registry_username"},
|
||||
Password: config.Value{Type: config.ValueTypeString, Value: "password2"},
|
||||
},
|
||||
},
|
||||
Runtime: &config.Runtime{
|
||||
Type: "pod",
|
||||
Auth: &config.RegistryAuth{
|
||||
Type: config.RegistryAuthTypeDefault,
|
||||
Username: config.Value{Type: config.ValueTypeString, Value: "username"},
|
||||
Password: config.Value{Type: config.ValueTypeFromVariable, Value: "password"},
|
||||
},
|
||||
Arch: "",
|
||||
Containers: []*config.Container{
|
||||
&config.Container{
|
||||
Image: "image01",
|
||||
Auth: &config.RegistryAuth{
|
||||
Type: config.RegistryAuthTypeDefault,
|
||||
Username: config.Value{Type: config.ValueTypeFromVariable, Value: "registry_username"},
|
||||
Password: config.Value{Type: config.ValueTypeString, Value: "password2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -860,20 +884,33 @@ func TestGenRunConfig(t *testing.T) {
|
|||
variables: map[string]string{
|
||||
"variable01": "VARVALUE01",
|
||||
"registry_username": "yourregistryusername",
|
||||
"password": "myregistrypassword",
|
||||
},
|
||||
out: map[string]*rstypes.RunConfigTask{
|
||||
uuid.New("task01").String(): &rstypes.RunConfigTask{
|
||||
ID: uuid.New("task01").String(),
|
||||
Name: "task01", Depends: map[string]*rstypes.RunConfigTaskDepend{},
|
||||
DockerRegistriesAuth: map[string]rstypes.DockerRegistryAuth{
|
||||
"index.docker.io": {
|
||||
Type: rstypes.DockerRegistryAuthTypeBasic,
|
||||
Username: "yourregistryusername",
|
||||
Password: "password2",
|
||||
},
|
||||
"https://myregistry.example.com": {
|
||||
Type: rstypes.DockerRegistryAuthTypeBasic,
|
||||
Username: "username",
|
||||
Password: "myregistrypassword",
|
||||
},
|
||||
"https://anotherregistry.example.com": {
|
||||
Type: rstypes.DockerRegistryAuthTypeBasic,
|
||||
Username: "yourregistryusername",
|
||||
Password: "password2",
|
||||
},
|
||||
},
|
||||
Runtime: &rstypes.Runtime{Type: rstypes.RuntimeType("pod"),
|
||||
Containers: []*rstypes.Container{
|
||||
{
|
||||
Image: "image01",
|
||||
Auth: &rstypes.RegistryAuth{
|
||||
Type: rstypes.RegistryAuthTypeDefault,
|
||||
Username: "yourregistryusername",
|
||||
Password: "password2",
|
||||
},
|
||||
Image: "image01",
|
||||
Environment: map[string]string{},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -16,6 +16,8 @@ package driver
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
@ -25,6 +27,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sorintlab/agola/internal/services/runservice/executor/registry"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
|
@ -116,9 +119,25 @@ func (d *DockerDriver) NewPod(ctx context.Context, podConfig *PodConfig, out io.
|
|||
|
||||
containerConfig := podConfig.Containers[0]
|
||||
|
||||
regName, err := registry.GetRegistry(containerConfig.Image)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var registryAuth registry.DockerConfigAuth
|
||||
if podConfig.DockerConfig != nil {
|
||||
if regauth, ok := podConfig.DockerConfig.Auths[regName]; ok {
|
||||
registryAuth = regauth
|
||||
}
|
||||
}
|
||||
buf, err := json.Marshal(registryAuth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
registryAuthEnc := base64.URLEncoding.EncodeToString(buf)
|
||||
|
||||
// by default always try to pull the image so we are sure only authorized users can fetch them
|
||||
// see https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#alwayspullimages
|
||||
reader, err := d.client.ImagePull(ctx, containerConfig.Image, types.ImagePullOptions{RegistryAuth: containerConfig.RegistryAuth})
|
||||
reader, err := d.client.ImagePull(ctx, containerConfig.Image, types.ImagePullOptions{RegistryAuth: registryAuthEnc})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@ package driver
|
|||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/sorintlab/agola/internal/services/runservice/executor/registry"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -68,16 +70,16 @@ type PodConfig struct {
|
|||
Labels map[string]string
|
||||
// The container dir where the init volume will be mounted
|
||||
InitVolumeDir string
|
||||
DockerConfig *registry.DockerConfig
|
||||
}
|
||||
|
||||
type ContainerConfig struct {
|
||||
Cmd []string
|
||||
Env map[string]string
|
||||
WorkingDir string
|
||||
Image string
|
||||
User string
|
||||
Privileged bool
|
||||
RegistryAuth string
|
||||
Cmd []string
|
||||
Env map[string]string
|
||||
WorkingDir string
|
||||
Image string
|
||||
User string
|
||||
Privileged bool
|
||||
}
|
||||
|
||||
type ExecConfig struct {
|
||||
|
|
|
@ -37,6 +37,7 @@ import (
|
|||
slog "github.com/sorintlab/agola/internal/log"
|
||||
"github.com/sorintlab/agola/internal/services/config"
|
||||
"github.com/sorintlab/agola/internal/services/runservice/executor/driver"
|
||||
"github.com/sorintlab/agola/internal/services/runservice/executor/registry"
|
||||
rsapi "github.com/sorintlab/agola/internal/services/runservice/scheduler/api"
|
||||
"github.com/sorintlab/agola/internal/services/runservice/types"
|
||||
"github.com/sorintlab/agola/internal/util"
|
||||
|
@ -795,7 +796,7 @@ func (e *Executor) setupTask(ctx context.Context, rt *runningTask) error {
|
|||
|
||||
log.Debugf("starting pod")
|
||||
|
||||
registryAuth, err := registryAuthToken(et.Containers[0].Auth)
|
||||
dockerConfig, err := registry.GenDockerConfig(et.DockerRegistriesAuth, []string{et.Containers[0].Image})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -803,14 +804,14 @@ func (e *Executor) setupTask(ctx context.Context, rt *runningTask) error {
|
|||
podConfig := &driver.PodConfig{
|
||||
Labels: createTaskLabels(et.ID),
|
||||
InitVolumeDir: toolboxContainerDir,
|
||||
DockerConfig: dockerConfig,
|
||||
Containers: []*driver.ContainerConfig{
|
||||
{
|
||||
Image: et.Containers[0].Image,
|
||||
Cmd: cmd,
|
||||
Env: et.Containers[0].Environment,
|
||||
User: et.Containers[0].User,
|
||||
Privileged: et.Containers[0].Privileged,
|
||||
RegistryAuth: registryAuth,
|
||||
Image: et.Containers[0].Image,
|
||||
Cmd: cmd,
|
||||
Env: et.Containers[0].Environment,
|
||||
User: et.Containers[0].User,
|
||||
Privileged: et.Containers[0].Privileged,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
// Copyright 2019 Sorint.lab
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package executor
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/sorintlab/agola/internal/services/runservice/types"
|
||||
|
||||
dtypes "github.com/docker/docker/api/types"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func registryAuthToken(auth *types.RegistryAuth) (string, error) {
|
||||
if auth == nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
switch auth.Type {
|
||||
case types.RegistryAuthTypeDefault:
|
||||
authConfig := dtypes.AuthConfig{
|
||||
Username: auth.Username,
|
||||
Password: auth.Password,
|
||||
}
|
||||
authConfigj, err := json.Marshal(authConfig)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return base64.URLEncoding.EncodeToString(authConfigj), nil
|
||||
|
||||
default:
|
||||
return "", errors.Errorf("unsupported registry auth type %q", auth.Type)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
// Copyright 2019 Sorint.lab
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package registry
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sorintlab/agola/internal/services/runservice/types"
|
||||
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
)
|
||||
|
||||
//func registryAuthToken(auth *types.DockerRegistryAuth) (string, error) {
|
||||
// if auth == nil {
|
||||
// return "", nil
|
||||
// }
|
||||
//
|
||||
// switch auth.Type {
|
||||
// case types.DockerRegistryAuthTypeBasic:
|
||||
// authConfig := dtypes.AuthConfig{
|
||||
// Username: auth.Username,
|
||||
// Password: auth.Password,
|
||||
// }
|
||||
// authConfigj, err := json.Marshal(authConfig)
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// return base64.URLEncoding.EncodeToString(authConfigj), nil
|
||||
//
|
||||
// default:
|
||||
// return "", errors.Errorf("unsupported registry auth type %q", auth.Type)
|
||||
// }
|
||||
//}
|
||||
|
||||
// Docker config represents the docker config.json format. We only consider the "auths" part
|
||||
type DockerConfig struct {
|
||||
Auths map[string]DockerConfigAuth `json:"auths,omitempty"`
|
||||
}
|
||||
|
||||
// Docker config represents the docker config.json auth part. We only consider the "auth" token part
|
||||
type DockerConfigAuth struct {
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
Auth string `json:"auth,omitempty"`
|
||||
}
|
||||
|
||||
// There are a variety of ways a domain may get qualified within the Docker credential file.
|
||||
// We enumerate them here as format strings.
|
||||
var (
|
||||
domainForms = []string{
|
||||
// Allow naked domains
|
||||
"%s",
|
||||
// Allow scheme-prefixed.
|
||||
"https://%s",
|
||||
"http://%s",
|
||||
// Allow scheme-prefixes with version in url path.
|
||||
"https://%s/v1/",
|
||||
"http://%s/v1/",
|
||||
"https://%s/v2/",
|
||||
"http://%s/v2/",
|
||||
}
|
||||
)
|
||||
|
||||
func GetRegistry(image string) (string, error) {
|
||||
ref, err := name.ParseReference(image, name.WeakValidation)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
regName := ref.Context().RegistryStr()
|
||||
return regName, nil
|
||||
}
|
||||
|
||||
// ResolveAuth resolves the auth username and password for the provided registry name
|
||||
func ResolveAuth(auths map[string]types.DockerRegistryAuth, regname string) (string, string, error) {
|
||||
if auths != nil {
|
||||
for _, form := range domainForms {
|
||||
if auth, ok := auths[fmt.Sprintf(form, regname)]; ok {
|
||||
switch auth.Type {
|
||||
case types.DockerRegistryAuthTypeEncodedAuth:
|
||||
decoded, err := base64.StdEncoding.DecodeString(auth.Auth)
|
||||
if err != nil {
|
||||
return "", "", errors.Wrapf(err, "failed to decode docker auth")
|
||||
}
|
||||
parts := strings.Split(string(decoded), ":")
|
||||
if len(parts) != 2 {
|
||||
return "", "", errors.Wrapf(err, "wrong docker auth")
|
||||
}
|
||||
return parts[0], parts[1], nil
|
||||
case types.DockerRegistryAuthTypeBasic:
|
||||
return auth.Username, auth.Password, nil
|
||||
default:
|
||||
return "", "", fmt.Errorf("unsupported auth type %q", auth.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", "", nil
|
||||
}
|
||||
|
||||
func GenDockerConfig(auths map[string]types.DockerRegistryAuth, images []string) (*DockerConfig, error) {
|
||||
dockerConfig := &DockerConfig{Auths: make(map[string]DockerConfigAuth)}
|
||||
for _, image := range images {
|
||||
ref, err := name.ParseReference(image, name.WeakValidation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
regName := ref.Context().RegistryStr()
|
||||
|
||||
if _, ok := dockerConfig.Auths[regName]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
username, password, err := ResolveAuth(auths, regName)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to resolve auth")
|
||||
}
|
||||
delimited := fmt.Sprintf("%s:%s", username, password)
|
||||
auth := base64.StdEncoding.EncodeToString([]byte(delimited))
|
||||
dockerConfig.Auths[regName] = DockerConfigAuth{Username: username, Password: password, Auth: auth}
|
||||
}
|
||||
|
||||
return dockerConfig, nil
|
||||
}
|
|
@ -335,6 +335,7 @@ func (s *Scheduler) genExecutorTask(ctx context.Context, r *types.Run, rt *types
|
|||
Steps: make([]*types.ExecutorTaskStepStatus, len(rct.Steps)),
|
||||
ExecutorID: executor.ID,
|
||||
},
|
||||
DockerRegistriesAuth: rct.DockerRegistriesAuth,
|
||||
}
|
||||
|
||||
for i := range et.Status.Steps {
|
||||
|
|
|
@ -317,19 +317,20 @@ func (rc *RunConfig) DeepCopy() *RunConfig {
|
|||
}
|
||||
|
||||
type RunConfigTask struct {
|
||||
Level int `json:"level,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Depends map[string]*RunConfigTaskDepend `json:"depends"`
|
||||
Runtime *Runtime `json:"runtime,omitempty"`
|
||||
Environment map[string]string `json:"environment,omitempty"`
|
||||
WorkingDir string `json:"working_dir,omitempty"`
|
||||
Shell string `json:"shell,omitempty"`
|
||||
User string `json:"user,omitempty"`
|
||||
Steps []interface{} `json:"steps,omitempty"`
|
||||
IgnoreFailure bool `json:"ignore_failure,omitempty"`
|
||||
NeedsApproval bool `json:"needs_approval,omitempty"`
|
||||
Skip bool `json:"skip,omitempty"`
|
||||
Level int `json:"level,omitempty"`
|
||||
ID string `json:"id,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Depends map[string]*RunConfigTaskDepend `json:"depends"`
|
||||
Runtime *Runtime `json:"runtime,omitempty"`
|
||||
Environment map[string]string `json:"environment,omitempty"`
|
||||
WorkingDir string `json:"working_dir,omitempty"`
|
||||
Shell string `json:"shell,omitempty"`
|
||||
User string `json:"user,omitempty"`
|
||||
Steps []interface{} `json:"steps,omitempty"`
|
||||
IgnoreFailure bool `json:"ignore_failure,omitempty"`
|
||||
NeedsApproval bool `json:"needs_approval,omitempty"`
|
||||
Skip bool `json:"skip,omitempty"`
|
||||
DockerRegistriesAuth map[string]DockerRegistryAuth `json:"docker_registries_auth"`
|
||||
}
|
||||
|
||||
type RunConfigTaskDependCondition string
|
||||
|
@ -351,18 +352,24 @@ const (
|
|||
RuntimeTypePod RuntimeType = "pod"
|
||||
)
|
||||
|
||||
type RegistryAuthType string
|
||||
type DockerRegistryAuthType string
|
||||
|
||||
const (
|
||||
RegistryAuthTypeDefault RegistryAuthType = "default"
|
||||
DockerRegistryAuthTypeBasic DockerRegistryAuthType = "basic"
|
||||
DockerRegistryAuthTypeEncodedAuth DockerRegistryAuthType = "encodedauth"
|
||||
)
|
||||
|
||||
type RegistryAuth struct {
|
||||
Type RegistryAuthType `json:"type,omitempty"`
|
||||
type DockerRegistryAuth struct {
|
||||
Type DockerRegistryAuthType `json:"type"`
|
||||
|
||||
// default auth
|
||||
Username string `json:"username,omitempty"`
|
||||
Password string `json:"password,omitempty"`
|
||||
// basic auth
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
|
||||
// encoded auth string
|
||||
Auth string `json:"auth"`
|
||||
|
||||
// future auths like aws ecr auth
|
||||
}
|
||||
|
||||
type Runtime struct {
|
||||
|
@ -502,6 +509,8 @@ type ExecutorTask struct {
|
|||
User string `json:"user,omitempty"`
|
||||
Privileged bool `json:"privileged"`
|
||||
|
||||
DockerRegistriesAuth map[string]DockerRegistryAuth `json:"docker_registries_auth"`
|
||||
|
||||
Steps []interface{} `json:"steps,omitempty"`
|
||||
|
||||
Status ExecutorTaskStatus `json:"status,omitempty"`
|
||||
|
@ -540,7 +549,6 @@ type ExecutorTaskStepStatus struct {
|
|||
|
||||
type Container struct {
|
||||
Image string `json:"image,omitempty"`
|
||||
Auth *RegistryAuth `json:"auth,omitempty"`
|
||||
Environment map[string]string `json:"environment,omitempty"`
|
||||
User string `json:"user,omitempty"`
|
||||
Privileged bool `json:"privileged"`
|
||||
|
|
Loading…
Reference in New Issue