mirror of https://github.com/docker/compose.git
add private images support
Signed-off-by: aiordache <anca.iordache@docker.com> Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
parent
57d7474f7d
commit
d09c8c7236
|
@ -16,7 +16,10 @@ import (
|
|||
)
|
||||
|
||||
type createSecretOptions struct {
|
||||
Label string
|
||||
Label string
|
||||
Username string
|
||||
Password string
|
||||
Description string
|
||||
}
|
||||
|
||||
type deleteSecretOptions struct {
|
||||
|
@ -39,9 +42,9 @@ func SecretCommand(dockerCli command.Cli) *cobra.Command {
|
|||
}
|
||||
|
||||
func CreateSecret(dockerCli command.Cli) *cobra.Command {
|
||||
//opts := createSecretOptions{}
|
||||
opts := createSecretOptions{}
|
||||
cmd := &cobra.Command{
|
||||
Use: "create NAME SECRET",
|
||||
Use: "create NAME",
|
||||
Short: "Creates a secret.",
|
||||
RunE: docker.WithAwsContext(dockerCli, func(clusteropts docker.AwsContext, args []string) error {
|
||||
client, err := amazon.NewClient(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
|
||||
|
@ -52,12 +55,16 @@ func CreateSecret(dockerCli command.Cli) *cobra.Command {
|
|||
return errors.New("Missing mandatory parameter: NAME")
|
||||
}
|
||||
name := args[0]
|
||||
secret := args[1]
|
||||
id, err := client.CreateSecret(context.Background(), name, secret)
|
||||
|
||||
secret := docker.NewSecret(name, opts.Username, opts.Password, opts.Description)
|
||||
id, err := client.CreateSecret(context.Background(), secret)
|
||||
fmt.Println(id)
|
||||
return err
|
||||
}),
|
||||
}
|
||||
cmd.Flags().StringVarP(&opts.Username, "username", "u", "", "username")
|
||||
cmd.Flags().StringVarP(&opts.Password, "password", "p", "", "password")
|
||||
cmd.Flags().StringVarP(&opts.Description, "description", "d", "", "Secret description")
|
||||
return cmd
|
||||
}
|
||||
|
||||
|
|
|
@ -55,17 +55,28 @@ func (c client) Convert(ctx context.Context, project *compose.Project) (*cloudfo
|
|||
}
|
||||
|
||||
taskExecutionRole := fmt.Sprintf("%sTaskExecutionRole", service.Name)
|
||||
policy, err := c.getPolicy(ctx, definition)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rolePolicies := []iam.Role_Policy{}
|
||||
if policy != nil {
|
||||
rolePolicies = append(rolePolicies, iam.Role_Policy{
|
||||
PolicyDocument: policy,
|
||||
PolicyName: taskExecutionRole,
|
||||
})
|
||||
|
||||
}
|
||||
definition.ExecutionRoleArn = cloudformation.Ref(taskExecutionRole)
|
||||
|
||||
taskDefinition := fmt.Sprintf("%sTaskDefinition", service.Name)
|
||||
template.Resources[taskExecutionRole] = &iam.Role{
|
||||
AssumeRolePolicyDocument: assumeRolePolicyDocument,
|
||||
// Here we can grant access to secrets/configs using a Policy { Allow,ssm:GetParameters,secret|config ARN}
|
||||
Policies: rolePolicies,
|
||||
ManagedPolicyArns: []string{
|
||||
ECSTaskExecutionPolicy,
|
||||
},
|
||||
}
|
||||
definition.ExecutionRoleArn = cloudformation.Ref(taskExecutionRole)
|
||||
// FIXME definition.TaskRoleArn = ?
|
||||
|
||||
taskDefinition := fmt.Sprintf("%sTaskDefinition", service.Name)
|
||||
template.Resources[taskDefinition] = definition
|
||||
|
||||
var healthCheck *cloudmap.Service_HealthCheckConfig
|
||||
|
@ -182,6 +193,33 @@ func (c client) GetVPC(ctx context.Context, project *compose.Project) (string, e
|
|||
return defaultVPC, nil
|
||||
}
|
||||
|
||||
func (c client) getPolicy(ctx context.Context, taskDef *ecs.TaskDefinition) (*PolicyDocument, error) {
|
||||
|
||||
arns := []string{}
|
||||
for _, container := range taskDef.ContainerDefinitions {
|
||||
if container.RepositoryCredentials != nil {
|
||||
arns = append(arns, container.RepositoryCredentials.CredentialsParameter)
|
||||
}
|
||||
if len(container.Secrets) > 0 {
|
||||
for _, s := range container.Secrets {
|
||||
arns = append(arns, s.ValueFrom)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if len(arns) > 0 {
|
||||
return &PolicyDocument{
|
||||
Statement: []PolicyStatement{
|
||||
{
|
||||
Effect: "Allow",
|
||||
Action: []string{"secretsmanager:GetSecretValue", "ssm:GetParameters", "kms:Decrypt"},
|
||||
Resource: arns,
|
||||
}},
|
||||
}, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type convertAPI interface {
|
||||
GetDefaultVPC(ctx context.Context) (string, error)
|
||||
VpcExists(ctx context.Context, vpcID string) (bool, error)
|
||||
|
|
|
@ -44,7 +44,7 @@ func Convert(project *compose.Project, service types.ServiceConfig) (*ecs.TaskDe
|
|||
FirelensConfiguration: nil,
|
||||
HealthCheck: toHealthCheck(service.HealthCheck),
|
||||
Hostname: service.Hostname,
|
||||
Image: service.Image,
|
||||
Image: getImage(service.Image),
|
||||
Interactive: false,
|
||||
Links: nil,
|
||||
LinuxParameters: toLinuxParameters(service),
|
||||
|
@ -282,22 +282,27 @@ func toKeyValuePair(environment types.MappingWithEquals) []ecs.TaskDefinition_Ke
|
|||
return pairs
|
||||
}
|
||||
|
||||
func getImage(image string) string {
|
||||
switch f := strings.Split(image, "/"); len(f) {
|
||||
case 1:
|
||||
return "docker.io/library/" + image
|
||||
case 2:
|
||||
return "docker.io/" + image
|
||||
default:
|
||||
return image
|
||||
}
|
||||
}
|
||||
|
||||
func getRepoCredentials(service types.ServiceConfig) (*ecs.TaskDefinition_RepositoryCredentials, error) {
|
||||
// extract registry and namespace string from image name
|
||||
fields := strings.Split(service.Image, "/")
|
||||
regPath := ""
|
||||
for i, field := range fields {
|
||||
if i < len(fields)-1 {
|
||||
regPath = regPath + field
|
||||
credential := ""
|
||||
for key, value := range service.Extras {
|
||||
if strings.HasPrefix(key, "x-aws-pull_credentials") {
|
||||
credential = value.(string)
|
||||
}
|
||||
}
|
||||
if regPath == "" || len(service.Secrets) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
for _, secret := range service.Secrets {
|
||||
if secret.Source == regPath {
|
||||
return &ecs.TaskDefinition_RepositoryCredentials{CredentialsParameter: secret.Target}, nil
|
||||
}
|
||||
if credential != "" {
|
||||
return &ecs.TaskDefinition_RepositoryCredentials{CredentialsParameter: credential}, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
@ -306,7 +311,7 @@ func getSecrets(service types.ServiceConfig) ([]ecs.TaskDefinition_Secret, error
|
|||
secrets := []ecs.TaskDefinition_Secret{}
|
||||
|
||||
for _, secret := range service.Secrets {
|
||||
secrets = append(secrets, ecs.TaskDefinition_Secret{Name: secret.Target})
|
||||
secrets = append(secrets, ecs.TaskDefinition_Secret{Name: secret.Target, ValueFrom: secret.Source})
|
||||
}
|
||||
return secrets, nil
|
||||
}
|
||||
|
|
|
@ -6,11 +6,12 @@ package mock
|
|||
|
||||
import (
|
||||
context "context"
|
||||
reflect "reflect"
|
||||
|
||||
cloudformation "github.com/aws/aws-sdk-go/service/cloudformation"
|
||||
cloudformation0 "github.com/awslabs/goformation/v4/cloudformation"
|
||||
docker "github.com/docker/ecs-plugin/pkg/docker"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
reflect "reflect"
|
||||
)
|
||||
|
||||
// MockAPI is a mock of API interface
|
||||
|
@ -67,18 +68,18 @@ func (mr *MockAPIMockRecorder) CreateCluster(arg0, arg1 interface{}) *gomock.Cal
|
|||
}
|
||||
|
||||
// CreateSecret mocks base method
|
||||
func (m *MockAPI) CreateSecret(arg0 context.Context, arg1, arg2 string) (string, error) {
|
||||
func (m *MockAPI) CreateSecret(arg0 context.Context, arg1 docker.Secret) (string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "CreateSecret", arg0, arg1, arg2)
|
||||
ret := m.ctrl.Call(m, "CreateSecret", arg0, arg1)
|
||||
ret0, _ := ret[0].(string)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// CreateSecret indicates an expected call of CreateSecret
|
||||
func (mr *MockAPIMockRecorder) CreateSecret(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
func (mr *MockAPIMockRecorder) CreateSecret(arg0, arg1 docker.Secret) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSecret", reflect.TypeOf((*MockAPI)(nil).CreateSecret), arg0, arg1, arg2)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSecret", reflect.TypeOf((*MockAPI)(nil).CreateSecret), arg0, arg1)
|
||||
}
|
||||
|
||||
// CreateStack mocks base method
|
||||
|
|
|
@ -223,9 +223,18 @@ func (s sdk) DeleteStack(ctx context.Context, name string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (s sdk) CreateSecret(ctx context.Context, name string, secret string) (string, error) {
|
||||
logrus.Debug("Create secret " + name)
|
||||
response, err := s.SM.CreateSecret(&secretsmanager.CreateSecretInput{Name: &name, SecretString: &secret})
|
||||
func (s sdk) CreateSecret(ctx context.Context, secret docker.Secret) (string, error) {
|
||||
logrus.Debug("Create secret " + secret.Name)
|
||||
secretStr, err := secret.GetCredString()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
response, err := s.SM.CreateSecret(&secretsmanager.CreateSecretInput{
|
||||
Name: &secret.Name,
|
||||
SecretString: &secretStr,
|
||||
Description: &secret.Description,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
|
@ -7,14 +7,14 @@ import (
|
|||
)
|
||||
|
||||
type secretsAPI interface {
|
||||
CreateSecret(ctx context.Context, name string, content string) (string, error)
|
||||
CreateSecret(ctx context.Context, secret docker.Secret) (string, error)
|
||||
InspectSecret(ctx context.Context, id string) (docker.Secret, error)
|
||||
ListSecrets(ctx context.Context) ([]docker.Secret, error)
|
||||
DeleteSecret(ctx context.Context, id string, recover bool) error
|
||||
}
|
||||
|
||||
func (c client) CreateSecret(ctx context.Context, name string, content string) (string, error) {
|
||||
return c.api.CreateSecret(ctx, name, content)
|
||||
func (c client) CreateSecret(ctx context.Context, secret docker.Secret) (string, error) {
|
||||
return c.api.CreateSecret(ctx, secret)
|
||||
}
|
||||
|
||||
func (c client) InspectSecret(ctx context.Context, id string) (docker.Secret, error) {
|
||||
|
|
|
@ -12,7 +12,7 @@ type API interface {
|
|||
ComposeUp(ctx context.Context, project *Project) error
|
||||
ComposeDown(ctx context.Context, projectName string, deleteCluster bool) error
|
||||
|
||||
CreateSecret(ctx context.Context, name string, secret string) (string, error)
|
||||
CreateSecret(ctx context.Context, secret docker.Secret) (string, error)
|
||||
InspectSecret(ctx context.Context, id string) (docker.Secret, error)
|
||||
ListSecrets(ctx context.Context) ([]docker.Secret, error)
|
||||
DeleteSecret(ctx context.Context, id string, recover bool) error
|
||||
|
|
|
@ -9,6 +9,17 @@ type Secret struct {
|
|||
Name string `json:"Name"`
|
||||
Labels map[string]string `json:"Labels"`
|
||||
Description string `json:"Description"`
|
||||
username string
|
||||
password string
|
||||
}
|
||||
|
||||
func NewSecret(name, username, password, description string) Secret {
|
||||
return Secret{
|
||||
Name: name,
|
||||
username: username,
|
||||
password: password,
|
||||
Description: description,
|
||||
}
|
||||
}
|
||||
|
||||
func (s Secret) ToJSON() (string, error) {
|
||||
|
@ -18,3 +29,15 @@ func (s Secret) ToJSON() (string, error) {
|
|||
}
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
func (s Secret) GetCredString() (string, error) {
|
||||
creds := map[string]string{
|
||||
"username": s.username,
|
||||
"password": s.password,
|
||||
}
|
||||
b, err := json.Marshal(&creds)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(b), nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue