190 lines
3.6 KiB
Go
190 lines
3.6 KiB
Go
package collector
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"io"
|
|
"os"
|
|
"path"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"localenv/utils"
|
|
|
|
"github.com/docker/docker/client"
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
const HostImagesStore string = "/tmp/localenv-swarm"
|
|
|
|
var ErrImageNotFound = errors.New("image not found")
|
|
|
|
func imageNameIntoFilename(name string) string {
|
|
name = strings.Replace(name, "/", "-", -1)
|
|
return strings.Replace(name, ":", "-", -1)
|
|
}
|
|
|
|
type collectorErr struct {
|
|
errs []error
|
|
sync.Mutex
|
|
}
|
|
|
|
func newCollectorError() collectorErr {
|
|
return collectorErr{
|
|
errs: []error{},
|
|
}
|
|
}
|
|
|
|
func (c *collectorErr) AddError(err error) {
|
|
c.Lock()
|
|
defer c.Unlock()
|
|
|
|
c.errs = append(c.errs, err)
|
|
}
|
|
|
|
func (c *collectorErr) Err() error {
|
|
if len(c.errs) != 0 {
|
|
return errors.Join(c.errs...)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type Collector struct {
|
|
hostCLI *client.Client
|
|
swarmCLI *client.Client
|
|
}
|
|
|
|
func NewCollector(hostCLI, swarmCLI *client.Client) Collector {
|
|
return Collector{
|
|
hostCLI: hostCLI,
|
|
swarmCLI: swarmCLI,
|
|
}
|
|
}
|
|
|
|
func (c Collector) saveImage(ctx context.Context, name, filepath string) error {
|
|
log.Info().Str("image", name).Str("path", filepath).Msg("saving image into store...")
|
|
|
|
imageSaveResponse, err := c.hostCLI.ImageSave(ctx, []string{name})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer imageSaveResponse.Close()
|
|
|
|
tarFile, err := os.Create(filepath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer tarFile.Close()
|
|
|
|
content, err := io.ReadAll(imageSaveResponse)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if _, err := tarFile.Write(content); err != nil {
|
|
return err
|
|
}
|
|
|
|
log.Info().Str("image", name).Str("path", filepath).Msg("image saved successfully")
|
|
return nil
|
|
}
|
|
|
|
func (c Collector) loadImage(ctx context.Context, filepath string) error {
|
|
log.Info().Str("path", filepath).Msg("loading image...")
|
|
|
|
tarFile, err := os.Open(filepath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer tarFile.Close()
|
|
|
|
loadResponse, err := c.swarmCLI.ImageLoad(ctx, tarFile, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer loadResponse.Body.Close()
|
|
|
|
log.Info().Str("path", filepath).Msg("image loaded successfully")
|
|
return nil
|
|
}
|
|
|
|
func (c Collector) retryCheckImage(ctx context.Context, name string) error {
|
|
fnRetry := func(ctx context.Context) error {
|
|
ok, err := c.checkImage(ctx, name)
|
|
if err != nil {
|
|
log.Warn().Str("image", name).Msg("error while searching image, retrying...")
|
|
return err
|
|
}
|
|
|
|
if !ok {
|
|
log.Warn().Str("image", name).Msg("image not found, retrying...")
|
|
return ErrImageNotFound
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
attempts := 30
|
|
waitDuration := 2 * time.Second
|
|
if err := utils.Retry(ctx, fnRetry, waitDuration, attempts); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c Collector) checkImage(ctx context.Context, name string) (bool, error) {
|
|
images, err := utils.FilterImagesByName(ctx, c.swarmCLI, name)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
if len(images) != 1 {
|
|
return false, nil
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
func (c Collector) DeployImages(ctx context.Context, images []string) error {
|
|
errs := newCollectorError()
|
|
|
|
var wg sync.WaitGroup
|
|
wg.Add(len(images))
|
|
|
|
for i := range images {
|
|
go func(idx int) {
|
|
defer wg.Done()
|
|
|
|
ok, err := c.checkImage(ctx, images[idx])
|
|
if err != nil {
|
|
errs.AddError(err)
|
|
}
|
|
|
|
if ok {
|
|
return
|
|
}
|
|
|
|
filepath := path.Join(HostImagesStore, imageNameIntoFilename(images[idx])+".tar")
|
|
|
|
if err = c.saveImage(ctx, images[idx], filepath); err != nil {
|
|
errs.AddError(err)
|
|
}
|
|
|
|
if err = c.loadImage(ctx, filepath); err != nil {
|
|
errs.AddError(err)
|
|
}
|
|
|
|
if err := c.retryCheckImage(ctx, images[idx]); err != nil {
|
|
errs.AddError(err)
|
|
}
|
|
}(i)
|
|
}
|
|
|
|
wg.Wait()
|
|
|
|
return errs.Err()
|
|
}
|