func (r GalaxyAdapter) getNextImages(ctx context.Context,
	url string, ch chan<- string,
	cancelFunc context.CancelFunc) (*GalaxySearchResponse, error) {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		log.Errorf("unable to get next roles for url: %v - %v", url, err)
		cancelFunc()
		close(ch)
		return nil, err
	}
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Errorf("unable to get next roles for url: %v - %v", url, err)
		cancelFunc()
		close(ch)
		return nil, err
	}
	defer resp.Body.Close()
	imageList, err := ioutil.ReadAll(resp.Body)
	iResp := GalaxySearchResponse{}
	err = json.Unmarshal(imageList, &iResp)
	if err != nil {
		log.Errorf("unable to get next images for url: %v - %v", url, err)
		cancelFunc()
		close(ch)
		return &iResp, err
	}
	if iResp.Next != "" {
		log.Debugf("getting next page of results - %v", iResp.Next)
		searchURL := fmt.Sprintf(galaxyAPIURL, r.Config.URL.String(), iResp.Next)
		go r.getNextImages(ctx, searchURL, ch, cancelFunc)
	}
	for _, imageName := range iResp.Results {
		log.Debugf("Trying to load %v.%v", imageName.Summary.Namespace.Name, imageName.Name)
		go func(image *GalaxyRole) {
			select {
			case <-ctx.Done():
				log.Debugf(
					"loading images failed due to context err - %v name - %v",
					ctx.Err(), image.Name)
				return
			default:
				ch <- fmt.Sprintf("%v.%v#%v", image.Summary.Namespace.Name, image.Name, image.RoleID)
			}
		}(imageName)
	}
	return &iResp, nil
}
getNextImages - will follow the next URL using go routines.
package adapters
import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/automationbroker/bundle-lib/bundle"
	log "github.com/sirupsen/logrus"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)
const (
	defaultRunner     = "ansibleplaybookbundle/apb-base:latest"
	defaultURL        = "https://galaxy.ansible.com"
	galaxyName        = "galaxy"
	galaxySearchURL   = "%v/api/v1/content/?content_type__name=apb"
	galaxyNSSearchURL = "%v/api/v1/content/?content_type__name=apb&namespace__name=%v"
	galaxyRoleURL     = "%v/api/v1/content/%v/"
	galaxyAPIURL      = "%v/api/v1%v"
)
type GalaxyAdapter struct {
	Config Configuration
}
type GalaxyRole struct {
	Name    string            `json:"name"`
	RoleID  int               `json:"id"`
	Summary GalaxyRoleSummary `json:"summary_fields"`
}
type GalaxyRoleResponse struct {
	Name     string             `json:"name"`
	Metadata GalaxyRoleMetadata `json:"metadata"`
	Summary  GalaxyRoleSummary  `json:"summary_fields"`
}
type GalaxyRoleMetadata struct {
	Spec bundle.Spec `json:"apb_metadata"`
}
type GalaxyRoleSummary struct {
	Namespace GalaxyRoleNamespace `json:"namespace"`
}
type GalaxyRoleNamespace struct {
	Name string `json:"name"`
}
type GalaxySearchResponse struct {
	Count   int           `json:"count"`
	Results []*GalaxyRole `json:"results"`
	Next    string        `json:"next"`
}
func (r GalaxyAdapter) RegistryName() string {
	return galaxyName
}
func (r *GalaxyAdapter) GetImageNames() ([]string, error) {
	log.Debug("GalaxyAdapter::GetImages")
	log.Debugf("BundleSpecLabel: %s", BundleSpecLabel)
	log.Debug("Loading role list with tag: [apb]")
	if r.Config.URL.Host == "" {
		log.Debugf("Using default galaxy url: %v", defaultURL)
		r.Config.URL, _ = url.Parse(defaultURL)
	}
	searchURL := fmt.Sprintf(galaxySearchURL, r.Config.URL.String())
	if len(r.Config.Org) != 0 {
		log.Debugf("Using namespaced galaxy search")
		searchURL = fmt.Sprintf(galaxyNSSearchURL, r.Config.URL.String(), r.Config.Org)
	}
	channel := make(chan string)
	ctx, cancelFunc := context.WithCancel(context.Background())
	defer cancelFunc()
	var imageResp *GalaxySearchResponse
	var err error
	imageResp, err = r.getNextImages(ctx, searchURL, channel, cancelFunc)
	if err != nil {
		return nil, err
	}
	if len(imageResp.Results) == 0 {
		log.Info("canceled retrieval as no items in org")
		close(channel)
	}
	var apbData []string
	counter := 1
	for imageData := range channel {
		apbData = append(apbData, imageData)
		if counter < imageResp.Count {
			counter++
		} else {
			close(channel)
		}
	}
	if ctx.Err() != nil {
		log.Errorf("encountered an error while loading images, we may not have all the apb in the catalog - %v", ctx.Err())
		return apbData, ctx.Err()
	}
	return apbData, nil
}
func (r GalaxyAdapter) FetchSpecs(imageNames []string) ([]*bundle.Spec, error) {
	if r.Config.URL.Host == "" {
		log.Debugf("Using default galaxy url: %v", defaultURL)
		r.Config.URL, _ = url.Parse(defaultURL)
	}
	specs := []*bundle.Spec{}
	for _, imageName := range imageNames {
		spec, err := r.loadSpec(imageName)
		if err != nil {
			log.Errorf("Failed to retrieve spec data for image %s - %v", imageName, err)
		}
		if spec != nil {
			specs = append(specs, spec)
		}
	}
	return specs, nil
}