github.com/oam-dev/kubevela@v1.9.11/pkg/builtin/build/build.go (about)

     1  /*
     2  Copyright 2021 The KubeVela Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package build
    18  
    19  import (
    20  	"encoding/json"
    21  	"io"
    22  	"os/exec"
    23  	"strings"
    24  
    25  	"github.com/pkg/errors"
    26  
    27  	"github.com/oam-dev/kubevela/pkg/builtin/kind"
    28  	"github.com/oam-dev/kubevela/pkg/builtin/registry"
    29  	cmdutil "github.com/oam-dev/kubevela/pkg/utils/util"
    30  )
    31  
    32  func init() {
    33  	registry.RegisterTask("build", ImageBuildHandler)
    34  }
    35  
    36  func ImageBuildHandler(ctx registry.CallCtx, params interface{}) error {
    37  	pm, err := json.Marshal(params)
    38  	if err != nil {
    39  		return err
    40  	}
    41  	b := new(Build)
    42  	if err := json.Unmarshal(pm, b); err != nil {
    43  		return err
    44  	}
    45  	v, err := ctx.LookUp("image")
    46  	if err != nil {
    47  		return err
    48  	}
    49  	image, ok := v.(string)
    50  	if !ok {
    51  		return errors.New("image must be 'string'")
    52  	}
    53  	if err := b.buildImage(ctx.IO(), image); err != nil {
    54  		return err
    55  	}
    56  	if err := b.pushImage(ctx.IO(), image); err != nil {
    57  		return err
    58  	}
    59  	return nil
    60  }
    61  
    62  // Build defines the build section of AppFile
    63  type Build struct {
    64  	Push   Push   `json:"push,omitempty"`
    65  	Docker Docker `json:"docker,omitempty"`
    66  }
    67  
    68  // Docker defines the docker build section
    69  type Docker struct {
    70  	File    string `json:"file"`
    71  	Context string `json:"context"`
    72  }
    73  
    74  // Push defines where to push your image
    75  type Push struct {
    76  	Local    string `json:"local,omitempty"`
    77  	Registry string `json:"registry,omitempty"`
    78  }
    79  
    80  func asyncLog(reader io.Reader, stream cmdutil.IOStreams) {
    81  	cache := ""
    82  	buf := make([]byte, 1024)
    83  	for {
    84  		num, err := reader.Read(buf)
    85  		if err != nil && errors.Is(err, io.EOF) {
    86  			return
    87  		}
    88  		if num > 0 {
    89  			b := buf[:num]
    90  			s := strings.Split(string(b), "\n")
    91  			line := strings.Join(s[:len(s)-1], "\n")
    92  			stream.Infof("%s%s\n", cache, line)
    93  			cache = s[len(s)-1]
    94  		}
    95  		if errors.Is(err, io.EOF) {
    96  			break
    97  		}
    98  	}
    99  }
   100  
   101  // buildImage will build a image with name and context.
   102  func (b *Build) buildImage(io cmdutil.IOStreams, image string) error {
   103  	//nolint:gosec
   104  	// keep docker binary command due to the issue #416 https://github.com/oam-dev/kubevela/issues/416
   105  	cmd := exec.Command("docker", "build", "-t", image, "-f", b.Docker.File, b.Docker.Context)
   106  	stdout, err := cmd.StdoutPipe()
   107  	if err != nil {
   108  		io.Errorf("BuildImage exec command error, message:%s\n", err.Error())
   109  		return err
   110  	}
   111  	stderr, err := cmd.StderrPipe()
   112  	if err != nil {
   113  		io.Errorf("BuildImage exec command error, message:%s\n", err.Error())
   114  		return err
   115  	}
   116  	if err := cmd.Start(); err != nil {
   117  		io.Errorf("BuildImage exec command error, message:%s\n", err.Error())
   118  		return err
   119  	}
   120  	go asyncLog(stdout, io)
   121  	go asyncLog(stderr, io)
   122  	if err := cmd.Wait(); err != nil {
   123  		io.Errorf("BuildImage wait for command execution error:%s", err.Error())
   124  		return err
   125  	}
   126  	return nil
   127  }
   128  
   129  func (b *Build) pushImage(io cmdutil.IOStreams, image string) error {
   130  	io.Infof("pushing image (%s)...\n", image)
   131  	switch {
   132  	case b.Push.Local == "kind":
   133  		err := kind.LoadDockerImage(image)
   134  		if err != nil {
   135  			io.Errorf("pushImage(kind) load docker image error, message:%s", err)
   136  			return err
   137  		}
   138  		return nil
   139  	default:
   140  	}
   141  	//nolint:gosec
   142  	cmd := exec.Command("docker", "push", image)
   143  	stdout, err := cmd.StdoutPipe()
   144  	if err != nil {
   145  		io.Errorf("pushImage(docker push) exec command error, message:%s\n", err.Error())
   146  		return err
   147  	}
   148  	stderr, err := cmd.StderrPipe()
   149  	if err != nil {
   150  		io.Errorf("pushImage(docker push) exec command error, message:%s\n", err.Error())
   151  		return err
   152  	}
   153  	if err := cmd.Start(); err != nil {
   154  		io.Errorf("pushImage(docker push) exec command error, message:%s\n", err.Error())
   155  		return err
   156  	}
   157  	go asyncLog(stdout, io)
   158  	go asyncLog(stderr, io)
   159  	if err := cmd.Wait(); err != nil {
   160  		io.Errorf("pushImage(docker push) wait for command execution error:%s", err.Error())
   161  		return err
   162  	}
   163  	return nil
   164  }