2018-01-10 13:35:05 +08:00
|
|
|
|
// RAINBOND, Application Management Platform
|
|
|
|
|
// Copyright (C) 2014-2017 Goodrain Co., Ltd.
|
|
|
|
|
|
|
|
|
|
// 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. For any non-GPL usage of Rainbond,
|
|
|
|
|
// one or multiple Commercial Licenses authorized by Goodrain Co., Ltd.
|
|
|
|
|
// must be obtained first.
|
|
|
|
|
|
|
|
|
|
// 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 sources
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bufio"
|
2018-03-01 18:30:24 +08:00
|
|
|
|
"bytes"
|
2018-03-01 13:34:54 +08:00
|
|
|
|
"encoding/base64"
|
|
|
|
|
"encoding/json"
|
2018-01-10 13:35:05 +08:00
|
|
|
|
"fmt"
|
2018-03-01 13:34:54 +08:00
|
|
|
|
"io"
|
2018-01-10 13:35:05 +08:00
|
|
|
|
"strings"
|
|
|
|
|
"time"
|
|
|
|
|
|
2018-02-28 15:14:27 +08:00
|
|
|
|
"github.com/Sirupsen/logrus"
|
|
|
|
|
|
2018-01-10 13:35:05 +08:00
|
|
|
|
"github.com/docker/distribution/reference"
|
2018-02-28 15:14:27 +08:00
|
|
|
|
"golang.org/x/net/context"
|
2018-02-07 11:36:24 +08:00
|
|
|
|
//"github.com/docker/docker/api/types"
|
|
|
|
|
"github.com/docker/engine-api/types"
|
2018-02-07 11:28:31 +08:00
|
|
|
|
//"github.com/docker/docker/client"
|
2018-03-01 18:30:24 +08:00
|
|
|
|
"github.com/docker/docker/pkg/archive"
|
|
|
|
|
"github.com/docker/docker/pkg/progress"
|
|
|
|
|
"github.com/docker/docker/pkg/streamformatter"
|
2018-02-07 11:28:31 +08:00
|
|
|
|
"github.com/docker/engine-api/client"
|
2018-01-25 22:10:34 +08:00
|
|
|
|
"github.com/goodrain/rainbond/pkg/builder/model"
|
2018-02-28 15:14:27 +08:00
|
|
|
|
"github.com/goodrain/rainbond/pkg/event"
|
2018-01-10 13:35:05 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
//ImagePull 拉取镜像
|
|
|
|
|
//timeout 分钟为单位
|
|
|
|
|
func ImagePull(dockerCli *client.Client, image string, opts types.ImagePullOptions, logger event.Logger, timeout int) (*types.ImageInspect, error) {
|
|
|
|
|
if logger != nil {
|
|
|
|
|
//进度信息
|
|
|
|
|
logger.Info(fmt.Sprintf("开始获取镜像:%s", image), map[string]string{"step": "pullimage"})
|
|
|
|
|
}
|
2018-02-07 14:50:15 +08:00
|
|
|
|
rf, err := reference.ParseAnyReference(image)
|
2018-01-10 13:35:05 +08:00
|
|
|
|
if err != nil {
|
2018-02-07 14:50:15 +08:00
|
|
|
|
logrus.Errorf("reference image error: %s", err.Error())
|
2018-01-10 13:35:05 +08:00
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
//最少一分钟
|
|
|
|
|
if timeout < 1 {
|
|
|
|
|
timeout = 1
|
|
|
|
|
}
|
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*time.Duration(timeout))
|
|
|
|
|
defer cancel()
|
2018-02-07 14:50:15 +08:00
|
|
|
|
//TODO: 使用1.12版本api的bug “repository name must be canonical”,使用rf.String()完整的镜像地址
|
|
|
|
|
readcloser, err := dockerCli.ImagePull(ctx, rf.String(), opts)
|
2018-01-10 13:35:05 +08:00
|
|
|
|
if err != nil {
|
2018-02-07 14:50:15 +08:00
|
|
|
|
logrus.Debugf("image name: %s readcloser error: %v", image, err.Error())
|
2018-01-10 13:35:05 +08:00
|
|
|
|
if strings.HasSuffix(err.Error(), "does not exist or no pull access") {
|
|
|
|
|
return nil, fmt.Errorf("Image(%s) does not exist or no pull access", image)
|
|
|
|
|
}
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
defer readcloser.Close()
|
|
|
|
|
r := bufio.NewReader(readcloser)
|
|
|
|
|
for {
|
|
|
|
|
if line, _, err := r.ReadLine(); err == nil {
|
|
|
|
|
if logger != nil {
|
|
|
|
|
//进度信息
|
|
|
|
|
logger.Debug(string(line), map[string]string{"step": "progress"})
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-02-07 11:36:24 +08:00
|
|
|
|
ins, _, err := dockerCli.ImageInspectWithRaw(ctx, image, false)
|
2018-01-10 13:35:05 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return &ins, nil
|
|
|
|
|
}
|
2018-01-17 22:06:58 +08:00
|
|
|
|
|
2018-01-25 22:10:34 +08:00
|
|
|
|
//ImageTag 修改镜像tag
|
|
|
|
|
func ImageTag(dockerCli *client.Client, source, target string, logger event.Logger, timeout int) error {
|
|
|
|
|
if logger != nil {
|
|
|
|
|
//进度信息
|
|
|
|
|
logger.Info(fmt.Sprintf("开始修改镜像tag:%s -> %s", source, target), map[string]string{"step": "changetag"})
|
|
|
|
|
}
|
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*time.Duration(timeout))
|
|
|
|
|
defer cancel()
|
|
|
|
|
err := dockerCli.ImageTag(ctx, source, target)
|
|
|
|
|
if err != nil {
|
|
|
|
|
logrus.Debugf("image tag err: %s", err.Error())
|
|
|
|
|
return err
|
|
|
|
|
}
|
2018-02-28 15:14:27 +08:00
|
|
|
|
logger.Info("镜像tag修改完成", map[string]string{"step": "changetag"})
|
2018-01-25 22:10:34 +08:00
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//ImageNameHandle 解析imagename
|
|
|
|
|
func ImageNameHandle(imageName string) *model.ImageName {
|
|
|
|
|
var i model.ImageName
|
2018-02-28 15:14:27 +08:00
|
|
|
|
if strings.Contains(imageName, "/") {
|
2018-01-25 22:10:34 +08:00
|
|
|
|
mm := strings.Split(imageName, "/")
|
|
|
|
|
i.Host = mm[0]
|
|
|
|
|
names := strings.Join(mm[1:], "/")
|
2018-02-28 15:14:27 +08:00
|
|
|
|
if strings.Contains(names, ":") {
|
2018-01-25 22:10:34 +08:00
|
|
|
|
nn := strings.Split(names, ":")
|
|
|
|
|
i.Name = nn[0]
|
|
|
|
|
i.Tag = nn[1]
|
2018-02-28 15:14:27 +08:00
|
|
|
|
} else {
|
2018-01-25 22:10:34 +08:00
|
|
|
|
i.Name = names
|
|
|
|
|
i.Tag = "latest"
|
|
|
|
|
}
|
2018-02-28 15:14:27 +08:00
|
|
|
|
} else {
|
|
|
|
|
if strings.Contains(imageName, ":") {
|
2018-01-25 22:10:34 +08:00
|
|
|
|
nn := strings.Split(imageName, ":")
|
|
|
|
|
i.Name = nn[0]
|
|
|
|
|
i.Tag = nn[1]
|
2018-02-28 15:14:27 +08:00
|
|
|
|
} else {
|
2018-01-25 22:10:34 +08:00
|
|
|
|
i.Name = imageName
|
|
|
|
|
i.Tag = "latest"
|
2018-02-28 15:14:27 +08:00
|
|
|
|
}
|
2018-01-25 22:10:34 +08:00
|
|
|
|
}
|
|
|
|
|
return &i
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-17 22:06:58 +08:00
|
|
|
|
//ImagePush 推送镜像
|
|
|
|
|
//timeout 分钟为单位
|
|
|
|
|
func ImagePush(dockerCli *client.Client, image string, opts types.ImagePushOptions, logger event.Logger, timeout int) error {
|
2018-01-25 22:10:34 +08:00
|
|
|
|
if logger != nil {
|
|
|
|
|
//进度信息
|
|
|
|
|
logger.Info(fmt.Sprintf("开始推送镜像:%s", image), map[string]string{"step": "pushimage"})
|
|
|
|
|
}
|
|
|
|
|
//最少一分钟
|
|
|
|
|
if timeout < 1 {
|
|
|
|
|
timeout = 1
|
|
|
|
|
}
|
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*time.Duration(timeout))
|
|
|
|
|
defer cancel()
|
|
|
|
|
readcloser, err := dockerCli.ImagePush(ctx, image, opts)
|
|
|
|
|
if err != nil {
|
|
|
|
|
if strings.HasSuffix(err.Error(), "does not exist") {
|
2018-03-01 13:34:54 +08:00
|
|
|
|
if logger != nil {
|
|
|
|
|
logger.Error(fmt.Sprintf("镜像:%s不存在,不能推送", image), map[string]string{"step": "pushimage"})
|
|
|
|
|
}
|
2018-01-25 22:10:34 +08:00
|
|
|
|
return fmt.Errorf("Image(%s) does not exist", image)
|
|
|
|
|
}
|
|
|
|
|
return err
|
2018-02-28 15:14:27 +08:00
|
|
|
|
}
|
2018-03-01 13:34:54 +08:00
|
|
|
|
if readcloser != nil {
|
|
|
|
|
defer readcloser.Close()
|
|
|
|
|
r := bufio.NewReader(readcloser)
|
|
|
|
|
for {
|
|
|
|
|
if line, _, err := r.ReadLine(); err == nil {
|
|
|
|
|
if logger != nil {
|
|
|
|
|
//进度信息
|
|
|
|
|
logger.Debug(string(line), map[string]string{"step": "progress"})
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
break
|
2018-01-25 22:10:34 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-02-28 15:14:27 +08:00
|
|
|
|
}
|
2018-01-17 22:06:58 +08:00
|
|
|
|
return nil
|
|
|
|
|
}
|
2018-01-29 11:25:49 +08:00
|
|
|
|
|
2018-03-01 13:34:54 +08:00
|
|
|
|
// EncodeAuthToBase64 serializes the auth configuration as JSON base64 payload
|
|
|
|
|
func EncodeAuthToBase64(authConfig types.AuthConfig) (string, error) {
|
|
|
|
|
buf, err := json.Marshal(authConfig)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
return base64.URLEncoding.EncodeToString(buf), nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ImagePushPrivileged push the image
|
|
|
|
|
func imagePushPrivileged(ctx context.Context, dockerCli *client.Client, authConfig types.AuthConfig, ref string, requestPrivilege types.RequestPrivilegeFunc) (io.ReadCloser, error) {
|
|
|
|
|
encodedAuth, err := EncodeAuthToBase64(authConfig)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
options := types.ImagePushOptions{
|
|
|
|
|
RegistryAuth: encodedAuth,
|
|
|
|
|
PrivilegeFunc: requestPrivilege,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return dockerCli.ImagePush(ctx, ref, options)
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-29 11:25:49 +08:00
|
|
|
|
//ImageBuild ImageBuild
|
2018-03-01 18:30:24 +08:00
|
|
|
|
func ImageBuild(dockerCli *client.Client, contextDir string, options types.ImageBuildOptions, logger event.Logger, timeout int) error {
|
2018-01-29 11:25:49 +08:00
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*time.Duration(timeout))
|
|
|
|
|
defer cancel()
|
2018-03-01 18:30:24 +08:00
|
|
|
|
buildCtx, err := archive.TarWithOptions(contextDir, &archive.TarOptions{
|
|
|
|
|
Compression: archive.Uncompressed,
|
|
|
|
|
ExcludePatterns: []string{""},
|
|
|
|
|
IncludeFiles: []string{"."},
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
progBuff := bytes.NewBuffer(nil)
|
|
|
|
|
go func() {
|
|
|
|
|
r := bufio.NewReader(progBuff)
|
|
|
|
|
for {
|
|
|
|
|
if line, _, err := r.ReadLine(); err == nil {
|
|
|
|
|
if logger != nil {
|
|
|
|
|
logger.Debug(string(line), map[string]string{"step": "dockerbuild"})
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
// Setup an upload progress bar
|
|
|
|
|
progressOutput := streamformatter.NewStreamFormatter().NewProgressOutput(progBuff, true)
|
|
|
|
|
var body io.Reader = progress.NewProgressReader(buildCtx, progressOutput, 0, "", "Sending build context to Docker daemon")
|
|
|
|
|
rc, err := dockerCli.ImageBuild(ctx, body, options)
|
2018-01-29 11:25:49 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
r := bufio.NewReader(rc.Body)
|
|
|
|
|
for {
|
|
|
|
|
if line, _, err := r.ReadLine(); err == nil {
|
|
|
|
|
if logger != nil {
|
2018-03-01 11:30:54 +08:00
|
|
|
|
logger.Debug(string(line), map[string]string{"step": "dockerbuild"})
|
2018-01-29 11:25:49 +08:00
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
break
|
|
|
|
|
}
|
2018-02-28 15:14:27 +08:00
|
|
|
|
}
|
2018-01-29 11:25:49 +08:00
|
|
|
|
return nil
|
2018-02-28 15:14:27 +08:00
|
|
|
|
}
|