mirror of
https://github.com/docker/compose.git
synced 2025-07-25 06:34:35 +02:00
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
@ -17,6 +17,9 @@ import (
|
|||||||
|
|
||||||
type createSecretOptions struct {
|
type createSecretOptions struct {
|
||||||
Label string
|
Label string
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
Description string
|
||||||
}
|
}
|
||||||
|
|
||||||
type deleteSecretOptions struct {
|
type deleteSecretOptions struct {
|
||||||
@ -39,9 +42,9 @@ func SecretCommand(dockerCli command.Cli) *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func CreateSecret(dockerCli command.Cli) *cobra.Command {
|
func CreateSecret(dockerCli command.Cli) *cobra.Command {
|
||||||
//opts := createSecretOptions{}
|
opts := createSecretOptions{}
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "create NAME SECRET",
|
Use: "create NAME",
|
||||||
Short: "Creates a secret.",
|
Short: "Creates a secret.",
|
||||||
RunE: docker.WithAwsContext(dockerCli, func(clusteropts docker.AwsContext, args []string) error {
|
RunE: docker.WithAwsContext(dockerCli, func(clusteropts docker.AwsContext, args []string) error {
|
||||||
client, err := amazon.NewClient(clusteropts.Profile, clusteropts.Cluster, clusteropts.Region)
|
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")
|
return errors.New("Missing mandatory parameter: NAME")
|
||||||
}
|
}
|
||||||
name := args[0]
|
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)
|
fmt.Println(id)
|
||||||
return err
|
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
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,17 +55,28 @@ func (c client) Convert(ctx context.Context, project *compose.Project) (*cloudfo
|
|||||||
}
|
}
|
||||||
|
|
||||||
taskExecutionRole := fmt.Sprintf("%sTaskExecutionRole", service.Name)
|
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{
|
template.Resources[taskExecutionRole] = &iam.Role{
|
||||||
AssumeRolePolicyDocument: assumeRolePolicyDocument,
|
AssumeRolePolicyDocument: assumeRolePolicyDocument,
|
||||||
// Here we can grant access to secrets/configs using a Policy { Allow,ssm:GetParameters,secret|config ARN}
|
Policies: rolePolicies,
|
||||||
ManagedPolicyArns: []string{
|
ManagedPolicyArns: []string{
|
||||||
ECSTaskExecutionPolicy,
|
ECSTaskExecutionPolicy,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
definition.ExecutionRoleArn = cloudformation.Ref(taskExecutionRole)
|
|
||||||
// FIXME definition.TaskRoleArn = ?
|
|
||||||
|
|
||||||
taskDefinition := fmt.Sprintf("%sTaskDefinition", service.Name)
|
|
||||||
template.Resources[taskDefinition] = definition
|
template.Resources[taskDefinition] = definition
|
||||||
|
|
||||||
var healthCheck *cloudmap.Service_HealthCheckConfig
|
var healthCheck *cloudmap.Service_HealthCheckConfig
|
||||||
@ -182,6 +193,33 @@ func (c client) GetVPC(ctx context.Context, project *compose.Project) (string, e
|
|||||||
return defaultVPC, nil
|
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 {
|
type convertAPI interface {
|
||||||
GetDefaultVPC(ctx context.Context) (string, error)
|
GetDefaultVPC(ctx context.Context) (string, error)
|
||||||
VpcExists(ctx context.Context, vpcID string) (bool, 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,
|
FirelensConfiguration: nil,
|
||||||
HealthCheck: toHealthCheck(service.HealthCheck),
|
HealthCheck: toHealthCheck(service.HealthCheck),
|
||||||
Hostname: service.Hostname,
|
Hostname: service.Hostname,
|
||||||
Image: service.Image,
|
Image: getImage(service.Image),
|
||||||
Interactive: false,
|
Interactive: false,
|
||||||
Links: nil,
|
Links: nil,
|
||||||
LinuxParameters: toLinuxParameters(service),
|
LinuxParameters: toLinuxParameters(service),
|
||||||
@ -282,22 +282,27 @@ func toKeyValuePair(environment types.MappingWithEquals) []ecs.TaskDefinition_Ke
|
|||||||
return pairs
|
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) {
|
func getRepoCredentials(service types.ServiceConfig) (*ecs.TaskDefinition_RepositoryCredentials, error) {
|
||||||
// extract registry and namespace string from image name
|
// extract registry and namespace string from image name
|
||||||
fields := strings.Split(service.Image, "/")
|
credential := ""
|
||||||
regPath := ""
|
for key, value := range service.Extras {
|
||||||
for i, field := range fields {
|
if strings.HasPrefix(key, "x-aws-pull_credentials") {
|
||||||
if i < len(fields)-1 {
|
credential = value.(string)
|
||||||
regPath = regPath + field
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if regPath == "" || len(service.Secrets) == 0 {
|
if credential != "" {
|
||||||
return nil, nil
|
return &ecs.TaskDefinition_RepositoryCredentials{CredentialsParameter: credential}, nil
|
||||||
}
|
|
||||||
for _, secret := range service.Secrets {
|
|
||||||
if secret.Source == regPath {
|
|
||||||
return &ecs.TaskDefinition_RepositoryCredentials{CredentialsParameter: secret.Target}, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@ -306,7 +311,7 @@ func getSecrets(service types.ServiceConfig) ([]ecs.TaskDefinition_Secret, error
|
|||||||
secrets := []ecs.TaskDefinition_Secret{}
|
secrets := []ecs.TaskDefinition_Secret{}
|
||||||
|
|
||||||
for _, secret := range service.Secrets {
|
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
|
return secrets, nil
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,12 @@ package mock
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
context "context"
|
context "context"
|
||||||
|
reflect "reflect"
|
||||||
|
|
||||||
cloudformation "github.com/aws/aws-sdk-go/service/cloudformation"
|
cloudformation "github.com/aws/aws-sdk-go/service/cloudformation"
|
||||||
cloudformation0 "github.com/awslabs/goformation/v4/cloudformation"
|
cloudformation0 "github.com/awslabs/goformation/v4/cloudformation"
|
||||||
docker "github.com/docker/ecs-plugin/pkg/docker"
|
docker "github.com/docker/ecs-plugin/pkg/docker"
|
||||||
gomock "github.com/golang/mock/gomock"
|
gomock "github.com/golang/mock/gomock"
|
||||||
reflect "reflect"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// MockAPI is a mock of API interface
|
// MockAPI is a mock of API interface
|
||||||
@ -67,18 +68,18 @@ func (mr *MockAPIMockRecorder) CreateCluster(arg0, arg1 interface{}) *gomock.Cal
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateSecret mocks base method
|
// 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()
|
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)
|
ret0, _ := ret[0].(string)
|
||||||
ret1, _ := ret[1].(error)
|
ret1, _ := ret[1].(error)
|
||||||
return ret0, ret1
|
return ret0, ret1
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateSecret indicates an expected call of CreateSecret
|
// 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()
|
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
|
// CreateStack mocks base method
|
||||||
|
@ -223,9 +223,18 @@ func (s sdk) DeleteStack(ctx context.Context, name string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s sdk) CreateSecret(ctx context.Context, name string, secret string) (string, error) {
|
func (s sdk) CreateSecret(ctx context.Context, secret docker.Secret) (string, error) {
|
||||||
logrus.Debug("Create secret " + name)
|
logrus.Debug("Create secret " + secret.Name)
|
||||||
response, err := s.SM.CreateSecret(&secretsmanager.CreateSecretInput{Name: &name, SecretString: &secret})
|
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 {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -7,14 +7,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type secretsAPI interface {
|
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)
|
InspectSecret(ctx context.Context, id string) (docker.Secret, error)
|
||||||
ListSecrets(ctx context.Context) ([]docker.Secret, error)
|
ListSecrets(ctx context.Context) ([]docker.Secret, error)
|
||||||
DeleteSecret(ctx context.Context, id string, recover bool) error
|
DeleteSecret(ctx context.Context, id string, recover bool) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c client) CreateSecret(ctx context.Context, name string, content string) (string, error) {
|
func (c client) CreateSecret(ctx context.Context, secret docker.Secret) (string, error) {
|
||||||
return c.api.CreateSecret(ctx, name, content)
|
return c.api.CreateSecret(ctx, secret)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c client) InspectSecret(ctx context.Context, id string) (docker.Secret, error) {
|
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
|
ComposeUp(ctx context.Context, project *Project) error
|
||||||
ComposeDown(ctx context.Context, projectName string, deleteCluster bool) 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)
|
InspectSecret(ctx context.Context, id string) (docker.Secret, error)
|
||||||
ListSecrets(ctx context.Context) ([]docker.Secret, error)
|
ListSecrets(ctx context.Context) ([]docker.Secret, error)
|
||||||
DeleteSecret(ctx context.Context, id string, recover bool) error
|
DeleteSecret(ctx context.Context, id string, recover bool) error
|
||||||
|
@ -9,6 +9,17 @@ type Secret struct {
|
|||||||
Name string `json:"Name"`
|
Name string `json:"Name"`
|
||||||
Labels map[string]string `json:"Labels"`
|
Labels map[string]string `json:"Labels"`
|
||||||
Description string `json:"Description"`
|
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) {
|
func (s Secret) ToJSON() (string, error) {
|
||||||
@ -18,3 +29,15 @@ func (s Secret) ToJSON() (string, error) {
|
|||||||
}
|
}
|
||||||
return string(b), nil
|
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…
x
Reference in New Issue
Block a user