You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: hatchery.go
    path of file: ./repos/cds/engine/hatchery/vsphere
    
    the code of the file until where you have to start completion: package vsphere

import (
	"context"
	"fmt"
	"strings"
	"sync"
	"time"

	jwt "github.com/golang-jwt/jwt"
	"github.com/gorilla/mux"
	"github.com/rockbears/log"
	"github.com/vmware/govmomi/vim25/mo"
	"github.com/vmware/govmomi/vim25/types"

	"github.com/ovh/cds/engine/api"
	"github.com/ovh/cds/engine/service"
	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/cdsclient"
	"github.com/ovh/cds/sdk/hatchery"
	"github.com/ovh/cds/sdk/namesgenerator"
	"github.com/ovh/cds/sdk/telemetry"
)

// New instanciates a new Hatchery vsphere
func New() *HatcheryVSphere {
	s := new(HatcheryVSphere)
	s.GoRoutines = sdk.NewGoRoutines(context.Background())
	return s
}

var _ hatchery.InterfaceWithModels = new(HatcheryVSphere)

// Init cdsclient config.
func (h *HatcheryVSphere) Init(config interface{}) (cdsclient.ServiceConfig, error) {
	var cfg cdsclient.ServiceConfig
	sConfig, ok := config.(HatcheryConfiguration)
	if !ok {
		return cfg, sdk.WithStack(fmt.Errorf("invalid vsphere hatchery configuration"))
	}
	h.Router = &api.Router{
		Mux:    mux.NewRouter(),
		Config: sConfig.HTTP,
	}
	cfg.Host = sConfig.API.HTTP.URL
	cfg.Token = sConfig.API.Token
	cfg.TokenV2 = sConfig.API.TokenV2
	cfg.InsecureSkipVerifyTLS = sConfig.API.HTTP.Insecure
	cfg.RequestSecondsTimeout = sConfig.API.RequestTimeout
	return cfg, nil
}

// ApplyConfiguration apply an object of type HatcheryConfiguration after checking it
func (h *HatcheryVSphere) ApplyConfiguration(cfg interface{}) error {
	if err := h.CheckConfiguration(cfg); err != nil {
		return err
	}

	var ok bool
	h.Config, ok = cfg.(HatcheryConfiguration)
	if !ok {
		return sdk.WithStack(fmt.Errorf("Invalid configuration"))
	}

	h.Common.Common.ServiceName = h.Config.Name
	h.Common.Common.ServiceType = sdk.TypeHatchery
	h.HTTPURL = h.Config.URL
	h.MaxHeartbeatFailures = h.Config.API.MaxHeartbeatFailures
	var err error
	h.Common.Common.PrivateKey, err = jwt.ParseRSAPrivateKeyFromPEM([]byte(h.Config.RSAPrivateKey))
	if err != nil {
		return sdk.WithStack(fmt.Errorf("unable to parse RSA private Key: %v", err))
	}
	h.Common.Common.Region = h.Config.Provision.Region
	h.Common.Common.IgnoreJobWithNoRegion = h.Config.Provision.IgnoreJobWithNoRegion
	h.Common.Common.ModelType = h.ModelType()

	if h.Config.WorkerTTL == 0 {
		h.Config.WorkerTTL = 120
	}
	if h.Config.WorkerRegistrationTTL == 0 {
		h.Config.WorkerRegistrationTTL = 10
	}

	return nil
}

// Status returns sdk.MonitoringStatus, implements interface service.Service
func (h *HatcheryVSphere) Status(ctx context.Context) *sdk.MonitoringStatus {
	m := h.NewMonitoringStatus()
	ws, err := h.WorkersStarted(ctx)
	if err != nil {
		ctx = log.ContextWithStackTrace(ctx, err)
		log.Warn(ctx, err.Error())
	}
	m.AddLine(sdk.MonitoringStatusLine{Component: "Workers", Value: fmt.Sprintf("%d/%d", len(ws), h.Config.Provision.MaxWorker), Status: sdk.MonitoringStatusOK})

	vms, err := h.vSphereClient.ListVirtualMachines(ctx)
	if err != nil {
		lo
    
func (h *HatcheryVSphere) provisioning(ctx context.Context) {
	h.cacheProvisioning.mu.Lock()

	// Count exiting provisionned machine for each model
	var mapAlreadyProvisionned = make(map[string]int)
	machines := h.getVirtualMachines(ctx)
	for _, machine := range machines {
		if !strings.HasPrefix(machine.Name, "provision-") {
			continue
		}
		annot := getVirtualMachineCDSAnnotation(ctx, machine)
		if annot == nil {
			continue
		}
		if annot.HatcheryName != h.Name() {
			continue
		}
		if annot.Provisioning {
			mapAlreadyProvisionned[annot.WorkerModelPath] = mapAlreadyProvisionned[annot.WorkerModelPath] + 1
		}
	}
	h.cacheProvisioning.mu.Unlock()

	// Count provision to create for each model
	mapToProvision := make(map[string]int)
	mapModels := make(map[string]sdk.Model)
	for i := range h.Config.WorkerProvisioning {
		modelPath := h.Config.WorkerProvisioning[i].ModelPath
		number := h.Config.WorkerProvisioning[i].Number
		if modelPath == "" || number == 0 {
			continue
		}

		tuple := strings.Split(modelPath, "/")
		if len(tuple) != 2 {
			log.Error(ctx, "invalid model name %q", modelPath)
			continue
		}

		model, err := h.Client.WorkerModelGet(tuple[0], tuple[1])
		if err != nil {
			ctx = sdk.ContextWithStacktrace(ctx, err)
			log.Warn(ctx, "unable to get model name %q: %v", modelPath, err)
			continue
		}

		if model.CheckRegistration || model.NeedRegistration {
			log.Info(ctx, "model %q needs registration. skip provisioning.", modelPath)
			continue
		}

		log.Info(ctx, "model %q provisioning: %d/%d", modelPath, mapAlreadyProvisionned[modelPath], number)

		mapModels[modelPath] = model
		count := int(number) - mapAlreadyProvisionned[modelPath]
		if count > 0 {
			mapToProvision[modelPath] = count
		}
	}

	// Distribute models in provision queue
	countModelToProvision := len(mapToProvision)
	if countModelToProvision == 0 {
		return
	}
	poolSize := h.Config.WorkerProvisioningPoolSize
	if poolSize == 0 {
		poolSize = 1
	}
	var provisionQueue []string
	for len(mapToProvision) > 0 {
		for i := range h.Config.WorkerProvisioning {
			modelPath := h.Config.WorkerProvisioning[i].ModelPath
			count, ok := mapToProvision[modelPath]
			if !ok {
				continue
			}
			if count == 0 {
				delete(mapToProvision, modelPath)
				continue
			}
			provisionQueue = append(provisionQueue, modelPath)
			mapToProvision[modelPath] = mapToProvision[modelPath] - 1
		}
	}

	// Provision workers
	wg := new(sync.WaitGroup)
	for i := 0; i < len(provisionQueue) && i < poolSize; i++ {
		modelPath := provisionQueue[i]
		wg.Add(1)
		go func() {
			defer wg.Done()

			workerName := namesgenerator.GenerateWorkerName(modelPath, "provision")

			h.cacheProvisioning.mu.Lock()
			h.cacheProvisioning.pending = append(h.cacheProvisioning.pending, workerName)
			h.cacheProvisioning.mu.Unlock()

			if err := h.ProvisionWorker(ctx, mapModels[modelPath], workerName); err != nil {
				ctx = log.ContextWithStackTrace(ctx, err)
				log.Error(ctx, "unable to provision model %q: %v", modelPath, err)
			}

			h.cacheProvisioning.mu.Lock()
			h.cacheProvisioning.pending = sdk.DeleteFromArray(h.cacheProvisioning.pending, workerName)
			h.cacheProvisioning.mu.Unlock()
		}()
	}
	wg.Wait()
}