func (cli *RegistryClient) SingleTagData(repoTags []string) ([]string, map[string]manifest.Manifest, error) {
	tagManifests := make(map[string]manifest.Manifest)
	for _, tagName := range repoTags {
		tagManifests[tagName] = manifest.Manifest{}
	}
	return repoTags, tagManifests, nil
}
SingleTagData gets data for a set of single tags
package client
import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"reflect"
	"time"
	log "github.com/sirupsen/logrus"
	"github.com/ivanilves/lstags/api/v1/registry/client/auth"
	"github.com/ivanilves/lstags/api/v1/registry/client/cache"
	"github.com/ivanilves/lstags/api/v1/registry/client/request"
	"github.com/ivanilves/lstags/tag"
	"github.com/ivanilves/lstags/tag/manifest"
)
var DefaultConcurrentRequests = 16
var DefaultRetryDelay = 2 * time.Second
const MaxConcurrentRequests = 256
type RegistryClient struct {
	registry string
	username string
	password string
	Config Config
	Token auth.Token
	RepoTokens map[string]auth.Token
}
type Config struct {
	ConcurrentRequests int
	WaitBetween time.Duration
	RetryRequests int
	RetryDelay time.Duration
	TraceRequests bool
	IsInsecure bool
}
func New(registry string, config Config) (*RegistryClient, error) {
	if config.ConcurrentRequests == 0 {
		config.ConcurrentRequests = DefaultConcurrentRequests
	}
	if config.RetryDelay == 0 {
		config.RetryDelay = DefaultRetryDelay
	}
	if config.ConcurrentRequests > MaxConcurrentRequests {
		err := fmt.Errorf(
			"Could not run more than %d concurrent requests (%d configured)",
			MaxConcurrentRequests,
			config.ConcurrentRequests,
		)
		return nil, err
	}
	return &RegistryClient{
		registry:   registry,
		Config:     config,
		RepoTokens: make(map[string]auth.Token),
	}, nil
}
func (cli *RegistryClient) webScheme() string {
	if cli.Config.IsInsecure {
		return "http://"
	}
	return "https://"
}
func (cli *RegistryClient) URL() string {
	return cli.webScheme() + cli.registry + "/v2/"
}
func (cli *RegistryClient) Ping() error {
	resp, err := http.Get(cli.URL())
	if err != nil {
		return err
	}
	if resp.StatusCode != 200 && resp.StatusCode != 401 {
		return fmt.Errorf("Unexpected status: %s", resp.Status)
	}
	return nil
}
func (cli *RegistryClient) registryToken(username, password string) (auth.Token, error) {
	tk, err := auth.NewToken(cli.URL(), username, password, "registry:catalog:*")
	if err != nil {
		if cli.Config.WaitBetween == 0 {
			log.Debugf("Try to login with less permissions (repository:catalog:*)")
		} else {
			log.Debugf("Try to login with less permissions (repository:catalog:*) [after waiting %v]", cli.Config.WaitBetween)
			time.Sleep(cli.Config.WaitBetween)
		}
		tk, err = auth.NewToken(cli.URL(), username, password, "repository:catalog:*")
		if err != nil {
			if username == "" && password == "" {
				return tk, nil
			}
			return tk, err
		}
	}
	return tk, nil
}
func (cli *RegistryClient) Login(username, password string) error {
	if !cache.Token.Exists(cli.registry) {
		tk, err := cli.registryToken(username, password)
		if err != nil {
			return err
		}
		cache.Token.Set(cli.registry, tk)
	}
	cli.Token = cache.Token.Get(cli.registry)
	cli.username = username
	cli.password = password
	return nil
}
func (cli *RegistryClient) IsLoggedIn() bool {
	return cli.Token != nil
}
func decodeAllTagData(body io.ReadCloser) ([]string, map[string]manifest.Manifest, error) {
	tagData := struct {
		TagNames     []string                `json:"tags"`
		RawManifests map[string]manifest.Raw `json:"manifest,omitempty"`
	}{}
	if err := json.NewDecoder(body).Decode(&tagData); err != nil {
		return nil, nil, err
	}
	tagManifests, err := manifest.ParseMap(tagData.RawManifests)
	if err != nil {
		return nil, nil, err
	}
	return tagData.TagNames, manifest.MapByTag(tagManifests), nil
}
func (cli *RegistryClient) repoToken(repoPath string) (auth.Token, error) {
	if cli.Token != nil && !reflect.ValueOf(cli.Token).IsNil() && cli.Token.Method() != "Bearer" {
		return cli.Token, nil
	}
	_, tokenDefined := cli.RepoTokens[repoPath]
	if tokenDefined {
		return cli.RepoTokens[repoPath], nil
	}
	if !cache.Token.Exists(repoPath) {
		repoToken, err := auth.NewToken(
			cli.URL(),
			cli.username,
			cli.password,
			"repository:"+repoPath+":pull",
		)
		if err != nil {
			return nil, err
		}
		cache.Token.Set(repoPath, repoToken)
	}
	cli.RepoTokens[repoPath] = cache.Token.Get(repoPath)
	return cli.RepoTokens[repoPath], nil
}
func (cli *RegistryClient) TagData(
	repoPath string,
	isSingle bool,
	repoTags []string,
) ([]string, map[string]manifest.Manifest, error) {
	if isSingle {
		return cli.SingleTagData(repoTags)
	}
	return cli.AllTagData(repoPath)
}
func (cli *RegistryClient) AllTagData(repoPath string) ([]string, map[string]manifest.Manifest, error) {
	repoToken, err := cli.repoToken(repoPath)
	if err != nil {
		return nil, nil, err
	}
	allTagNames := make([]string, 0)
	allTagManifests := make(map[string]manifest.Manifest)
	link := "/tags/list"
	for {
		resp, nextlink, err := request.Perform(
			cli.URL()+repoPath+link,
			repoToken.Method()+" "+repoToken.String(),
			"v2",
			cli.Config.TraceRequests,
			cli.Config.RetryRequests,
			cli.Config.RetryDelay,
		)
		if err != nil {
			return nil, nil, err
		}
		tagNames, tagManifests, err := decodeAllTagData(resp.Body)
		if err != nil {
			return nil, nil, err
		}
		allTagNames = append(allTagNames, tagNames...)
		allTagManifests = manifest.MergeMaps(allTagManifests, tagManifests)
		if nextlink == "" {
			break
		}
		link = "/tags/list?" + nextlink
	}
	return allTagNames, allTagManifests, nil
}