<commit_before>/*
// Copyright (c) 2016 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/

package main

import (
	"fmt"
	"net"
	"runtime"
	"sync"
	"time"

	"github.com/ciao-project/ciao/ciao-controller/types"
	"github.com/ciao-project/ciao/payloads"
	"github.com/golang/glog"
	"github.com/pkg/errors"
)

func (c *controller) restartInstance(instanceID string) error {
	// should I bother to see if instanceID is valid?
	i, err := c.ds.GetInstance(instanceID)
	if err != nil {
		return err
	}

	if i.State != "exited" {
		return errors.New("You may only restart paused instances")
	}

	w, err := c.ds.GetWorkload(i.TenantID, i.WorkloadID)
	if err != nil {
		return err
	}

	t, err := c.ds.GetTenant(i.TenantID)
	if err != nil {
		return err
	}

	if !i.CNCI {
		err = t.CNCIctrl.WaitForActive(i.Subnet)
		if err != nil {
			return errors.Wrap(err, "Error waiting for active subnet")
		}
	}

	go func() {
		if err := c.client.RestartInstance(i, &w, t); err != nil {
			glog.Warningf("Error restarting instance: %v", err)
		}
	}()

	return nil
}

func (c *controller) stopInstance(instanceID string) error {
	// get node id.  If there is no node id we can't send a delete
	i, err := c.ds.GetInstance(instanceID)
	if err != nil {
		return err
	}

	if i.NodeID == "" {
		return types.ErrInstanceNotAssigned
	}

	if i.State == payloads.ComputeStatusPending {
		return errors.New("You may not stop a pending instance")
	}

	go func() {
		if err := c.client.StopInstance(instanceID, i.NodeID); err != nil {
			glog.Warningf("Error stopping instance: %v", err)
		}
	}()

	return nil
}

// delete an instance, wait for the deleted event.
func (c *controller) deleteInstanceSync(instanceID string) error {
	wait := make(chan struct{})

	i, err := c.ds.GetInstance(instanceID)
	if err != nil {
		return err
	}

	err = c.deleteInstance(instanceID)
	if err != nil {
		return err
	}

	go func() {
		i.StateChange.L.Lock()
		for {
			i.StateLock.RLock()
			if i.State == payloads.Deleted || i.State == payloads.Hung {
				break
			}
			glog.V(2).Infof("waiting for %s to be deleted", i.ID)
			i.StateLock.RUnlock()
			i.StateChange.Wait()
		}

		i.StateLock.RUnlock()
		i.StateChange.L.Unlock()

		glog.V(2).Infof("%s is hung or deleted", i.ID)
		close(wait)
	}()

	select {
	case <-wait:
		return nil
	case <-time.After(2 * time.Minute):
		err = transitionInstanceState(i, payloads.Hung)
		if err != nil {
			glog.Warningf("Error transitioning instance to hung state: %v", err)
		}
		return fmt.Errorf("timeout waiting for delete")
	}
}

func (c *controller) deleteInstance(instanceID string) error {
	// get node id.  If there is no node id and the instance is
	// pending we can't send a delete
	i, err := c.ds.GetInstance(instanceID)
	if err != nil {
		return err
	}

	if i.NodeID == "" && i.State == payloads.Pending {
		return types.ErrInstanceNotAssigned
	}

	// check for any external IPs
	IPs := c.ds.GetMappedIPs(&i.TenantID)
	for _, m := range IPs {
		if m.InstanceID == instanceID {
			return types.ErrInstanceMapped
		}
	}

	go func() {
		if err := c.client.DeleteInstance(instanceID, i.NodeID); err != nil {
			glog.Warningf("Error deleting instance: %v", err)
		}
	}()

	return nil
}

func (c *controller) confirmTenantRaw(tenantID string) error {
	tenant, err := c.ds.GetTenant(tenantID)
	if err != nil {
		return err
	}

	if tenant != nil {
		return nil
	}

	// if we are adding tenant this way, we need to use defaults
	config := types.TenantConfig{
		Name:       "",
		SubnetBits: 24,
	}

	tenant, err = c.ds.AddTenant(tenantID, config)
	if err != nil {
		return err
	}

	tenant.CNCIctrl, err = newCNCIManager(c, tenantID)
	if err != nil {
		return err
	}

	return nil
}

func (c *controller) confirmTenant(tenantID string) error {
	c.tenantReadinessLock.Lock()
	memo := c.tenantReadiness[tenantID]
	if memo != nil {

		// Someone else has already or is in the process of confirming
		// this tenant.  We need to wait until memo.ch is closed before
		// continuing.

		c.tenantReadinessLock.Unlock()
		<-memo.ch
		if memo.err != nil {
			return memo.err
		}

		// If we get here we know that confirmTenantRaw has already
		// been successfully called for this tenant during the life
		// time of this controller invocation.

		return nil
	}

	ch := make(chan struct{})
	c.tenantReadiness[tenantID] = &tenantConfirmMemo{ch: ch}
	c.tenantReadinessLock.Unlock()
	err := c.confirmTenantRaw(tenantID)
	if err != nil {
		c.tenantReadinessLock.Lock()
		c.tenantReadiness[tenantID].err = err
		delete(c.tenantReadiness, tenantID)
		c.tenantReadinessLock.Unlock()
	}
	close(ch)
	return err
}

func (c *controller) startWorkload(w types.WorkloadRequest) ([]*types.Instance, error) {
	var e error
	var sem = make(chan int, runtime.NumCPU())

	if w.Instances <= 0 {
		return nil, errors.New("Missing number of instances to start")
	}

	wl, err := c.ds.GetWorkload(w.TenantID, w.WorkloadID)
	if err != nil {
		return nil, err
	}

	err = c.confirmTenant(w.TenantID)
	if err != nil {
		return nil, err
	}

	var IPPool []net.IP

	// if this is for a CNCI, we don't want to allocate any IPs.
	if w.Subnet == "" {
		IPPool, err = c.ds.AllocateTenantIPPool(w.TenantID, w.Instances)
		if err != nil {
			return nil, err
		}
	}

	var newInstances []*types.Instance
	var wg sync.WaitGroup

	for i := 0; i < w.Instances && e == nil; i++ {
		var newIP net.IP

		if w.Subnet == "" {
			newIP = IPPool[i]
		}
		wg.Add(1)
		go func(newIP net.IP) {
			sem <- 1
			defer func() {
				<-sem
				wg.Done()
			}()

			startTime := time.Now()

			name := w.Name
			if name != "" {
				if w.Instances > 1 {
					name = fmt.Sprintf("%s-%d", name, i)
				}
			}

			instance, err := newInstance(c, w.TenantID, &wl, w.Volumes, name, w.Subnet, newIP)
			if err != nil {
				e = errors.Wrap(err, "Error creating instance")
				return
			}
			instance.startTime = startTime

			ok, err := instance.Allowed()
			if err != nil {
				_ = instance.Clean()
				e = errors.Wrap(err, "Error checking if instance allowed")
				return
			}

			if ok {
				err = instance.Add()
				if err != nil {
					_ = instance.Clean()
					e = errors.Wrap(err, "Error adding instance")
					return
				}

				newInstances = append(newInstances, instance.Instance)
				go func(label string) {
					var err error
					if label == "" {
						err = c.client.StartWorkload(instance.newConfig.config)
					} else {
						err = c.client.StartTracedWorkload(instance.newConfig.config, instance.startTime, w.TraceLabel)
					}

					if err != nil {
						glog.Warningf("Error starting workload: %v", err)
					}
				}(w.TraceLabel)
			} else {
				_ = instance.Clean()
				// stop if we are over limits
				e = errors.New("Over quota")
				return
			}
		}(newIP)
	}

	wg.Wait()

	return newInstances, e
}

func (c *controller) deleteEphemeralStorage(instanceID string) error {
	attachments := c.ds.GetStorageAttachments(instanceID)
	for _, attachment := range attachments {
		if !attachment.Ephemeral {
			continue
		}
		err := c.ds.DeleteStorageAttachment(attachment.ID)
		if err != nil {
			return errors.Wrap(err, "Error deleting storage attachment from datastore")
		}
		bd, err := c.ds.GetBlockDevice(attachment.BlockID)
		if err != nil {
			return errors.Wrap(err, "Error getting block device from datastore")
		}
		err = c.ds.DeleteBlockDevice(attachment.BlockID)
		if err != nil {
			return errors.Wrap(err, "Error deleting block device from datastore")
		}
		err = c.DeleteBlockDevice(attachment.BlockID)
		if err != nil {
			return errors.Wrap(err, "Error deleting block device")
		}
		if !bd.Internal {
			c.qs.Release(bd.TenantID,
				payloads.RequestedResource{Type: payloads.Volume, Value: 1},
				payloads.RequestedResource{Type: payloads.SharedDiskGiB, Value: bd.Size})
		}
	}
	return nil
}
<commit_msg>ciao-controller: refactor startWorkload to reduce complexity.<commit_after>/*
// Copyright (c) 2016 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/

package main

import (
	"fmt"
	"net"
	"runtime"
	"time"

	"github.com/ciao-project/ciao/ciao-controller/types"
	"github.com/ciao-project/ciao/payloads"
	"github.com/golang/glog"
	"github.com/pkg/errors"
)

func (c *controller) restartInstance(instanceID string) error {
	// should I bother to see if instanceID is valid?
	i, err := c.ds.GetInstance(instanceID)
	if err != nil {
		return err
	}

	if i.State != "exited" {
		return errors.New("You may only restart paused instances")
	}

	w, err := c.ds.GetWorkload(i.TenantID, i.WorkloadID)
	if err != nil {
		return err
	}

	t, err := c.ds.GetTenant(i.TenantID)
	if err != nil {
		return err
	}

	if !i.CNCI {
		err = t.CNCIctrl.WaitForActive(i.Subnet)
		if err != nil {
			return errors.Wrap(err, "Error waiting for active subnet")
		}
	}

	go func() {
		if err := c.client.RestartInstance(i, &w, t); err != nil {
			glog.Warningf("Error restarting instance: %v", err)
		}
	}()

	return nil
}

func (c *controller) stopInstance(instanceID string) error {
	// get node id.  If there is no node id we can't send a delete
	i, err := c.ds.GetInstance(instanceID)
	if err != nil {
		return err
	}

	if i.NodeID == "" {
		return types.ErrInstanceNotAssigned
	}

	if i.State == payloads.ComputeStatusPending {
		return errors.New("You may not stop a pending instance")
	}

	go func() {
		if err := c.client.StopInstance(instanceID, i.NodeID); err != nil {
			glog.Warningf("Error stopping instance: %v", err)
		}
	}()

	return nil
}

// delete an instance, wait for the deleted event.
func (c *controller) deleteInstanceSync(instanceID string) error {
	wait := make(chan struct{})

	i, err := c.ds.GetInstance(instanceID)
	if err != nil {
		return err
	}

	err = c.deleteInstance(instanceID)
	if err != nil {
		return err
	}

	go func() {
		i.StateChange.L.Lock()
		for {
			i.StateLock.RLock()
			if i.State == payloads.Deleted || i.State == payloads.Hung {
				break
			}
			glog.V(2).Infof("waiting for %s to be deleted", i.ID)
			i.StateLock.RUnlock()
			i.StateChange.Wait()
		}

		i.StateLock.RUnlock()
		i.StateChange.L.Unlock()

		glog.V(2).Infof("%s is hung or deleted", i.ID)
		close(wait)
	}()

	select {
	case <-wait:
		return nil
	case <-time.After(2 * time.Minute):
		err = transitionInstanceState(i, payloads.Hung)
		if err != nil {
			glog.Warningf("Error transitioning instance to hung state: %v", err)
		}
		return fmt.Errorf("timeout waiting for delete")
	}
}

func (c *controller) deleteInstance(instanceID string) error {
	// get node id.  If there is no node id and the instance is
	// pending we can't send a delete
	i, err := c.ds.GetInstance(instanceID)
	if err != nil {
		return err
	}

	if i.NodeID == "" && i.State == payloads.Pending {
		return types.ErrInstanceNotAssigned
	}

	// check for any external IPs
	IPs := c.ds.GetMappedIPs(&i.TenantID)
	for _, m := range IPs {
		if m.InstanceID == instanceID {
			return types.ErrInstanceMapped
		}
	}

	go func() {
		if err := c.client.DeleteInstance(instanceID, i.NodeID); err != nil {
			glog.Warningf("Error deleting instance: %v", err)
		}
	}()

	return nil
}

func (c *controller) confirmTenantRaw(tenantID string) error {
	tenant, err := c.ds.GetTenant(tenantID)
	if err != nil {
		return err
	}

	if tenant != nil {
		return nil
	}

	// if we are adding tenant this way, we need to use defaults
	config := types.TenantConfig{
		Name:       "",
		SubnetBits: 24,
	}

	tenant, err = c.ds.AddTenant(tenantID, config)
	if err != nil {
		return err
	}

	tenant.CNCIctrl, err = newCNCIManager(c, tenantID)
	if err != nil {
		return err
	}

	return nil
}

func (c *controller) confirmTenant(tenantID string) error {
	c.tenantReadinessLock.Lock()
	memo := c.tenantReadiness[tenantID]
	if memo != nil {

		// Someone else has already or is in the process of confirming
		// this tenant.  We need to wait until memo.ch is closed before
		// continuing.

		c.tenantReadinessLock.Unlock()
		<-memo.ch
		if memo.err != nil {
			return memo.err
		}

		// If we get here we know that confirmTenantRaw has already
		// been successfully called for this tenant during the life
		// time of this controller invocation.

		return nil
	}

	ch := make(chan struct{})
	c.tenantReadiness[tenantID] = &tenantConfirmMemo{ch: ch}
	c.tenantReadinessLock.Unlock()
	err := c.confirmTenantRaw(tenantID)
	if err != nil {
		c.tenantReadinessLock.Lock()
		c.tenantReadiness[tenantID].err = err
		delete(c.tenantReadiness, tenantID)
		c.tenantReadinessLock.Unlock()
	}
	close(ch)
	return err
}

func (c *controller) createInstance(w types.WorkloadRequest, wl types.Workload, name string, newIP net.IP) (*types.Instance, error) {
	startTime := time.Now()

	instance, err := newInstance(c, w.TenantID, &wl, w.Volumes, name, w.Subnet, newIP)
	if err != nil {
		return nil, errors.Wrap(err, "Error creating instance")
	}
	instance.startTime = startTime

	ok, err := instance.Allowed()
	if err != nil {
		_ = instance.Clean()
		return nil, errors.Wrap(err, "Error checking if instance allowed")
	}

	if !ok {
		_ = instance.Clean()
		return nil, errors.New("Over quota")
	}

	err = instance.Add()
	if err != nil {
		_ = instance.Clean()
		return nil, errors.Wrap(err, "Error adding instance")
	}

	if w.TraceLabel == "" {
		err = c.client.StartWorkload(instance.newConfig.config)
	} else {
		err = c.client.StartTracedWorkload(instance.newConfig.config, instance.startTime, w.TraceLabel)
	}

	if err != nil {
		_ = instance.Clean()
		return nil, errors.Wrap(err, "Error starting workload")
	}

	return instance.Instance, nil
}

func (c *controller) startWorkload(w types.WorkloadRequest) ([]*types.Instance, error) {
	var e error
	var sem = make(chan int, runtime.NumCPU())

	if w.Instances <= 0 {
		return nil, errors.New("Missing number of instances to start")
	}

	wl, err := c.ds.GetWorkload(w.TenantID, w.WorkloadID)
	if err != nil {
		return nil, err
	}

	err = c.confirmTenant(w.TenantID)
	if err != nil {
		return nil, err
	}

	var IPPool []net.IP

	// if this is for a CNCI, we don't want to allocate any IPs.
	if w.Subnet == "" {
		IPPool, err = c.ds.AllocateTenantIPPool(w.TenantID, w.Instances)
		if err != nil {
			return nil, err
		}
	}

	var newInstances []*types.Instance
	type result struct {
		instance *types.Instance
		err      error
	}

	errChan := make(chan result)

	for i := 0; i < w.Instances; i++ {
		var newIP net.IP

		if w.Subnet == "" {
			newIP = IPPool[i]
		}

		name := w.Name
		if name != "" {
			if w.Instances > 1 {
				name = fmt.Sprintf("%s-%d", name, i)
			}
		}

		go func(newIP net.IP, name string) {
			sem <- 1
			var err error
			var instance *types.Instance
			defer func() {
				ret := result{
					err:      err,
					instance: instance,
				}
				<-sem
				errChan <- ret
			}()

			instance, err = c.createInstance(w, wl, name, newIP)
			if err != nil {
				err = errors.Wrap(err, "Error creating instance")
				return
			}
		}(newIP, name)
	}

	for i := 0; i < w.Instances; i++ {
		retVal := <-errChan
		if e == nil {
			// return the first error
			e = retVal.err
		}
		newInstances = append(newInstances, retVal.instance)
	}

	return newInstances, e
}

func (c *controller) deleteEphemeralStorage(instanceID string) error {
	attachments := c.ds.GetStorageAttachments(instanceID)
	for _, attachment := range attachments {
		if !attachment.Ephemeral {
			continue
		}
		err := c.ds.DeleteStorageAttachment(attachment.ID)
		if err != nil {
			return errors.Wrap(err, "Error deleting storage attachment from datastore")
		}
		bd, err := c.ds.GetBlockDevice(attachment.BlockID)
		if err != nil {
			return errors.Wrap(err, "Error getting block device from datastore")
		}
		err = c.ds.DeleteBlockDevice(attachment.BlockID)
		if err != nil {
			return errors.Wrap(err, "Error deleting block device from datastore")
		}
		err = c.DeleteBlockDevice(attachment.BlockID)
		if err != nil {
			return errors.Wrap(err, "Error deleting block device")
		}
		if !bd.Internal {
			c.qs.Release(bd.TenantID,
				payloads.RequestedResource{Type: payloads.Volume, Value: 1},
				payloads.RequestedResource{Type: payloads.SharedDiskGiB, Value: bd.Size})
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package logging

import (
	"github.com/caarlos0/log"
	"github.com/charmbracelet/lipgloss"
	"github.com/goreleaser/goreleaser/internal/middleware"
	"github.com/goreleaser/goreleaser/pkg/context"
)

var bold = lipgloss.NewStyle().Bold(true)

// Log pretty prints the given action and its title.
func Log(title string, next middleware.Action) middleware.Action {
	return func(ctx *context.Context) error {
		defer func() {
			log.ResetPadding()
		}()
		log.Infof(bold.Render(title))
		log.IncreasePadding()
		return next(ctx)
	}
}

// PadLog pretty prints the given action and its title with an increased padding.
func PadLog(title string, next middleware.Action) middleware.Action {
	return func(ctx *context.Context) error {
		defer log.ResetPadding()
		log.IncreasePadding()
		log.Infof(bold.Render(title))
		log.IncreasePadding()
		return next(ctx)
	}
}
<commit_msg>feat: log duration of slow pipes (#3173)<commit_after>package logging

import (
	"fmt"
	"time"

	"github.com/caarlos0/log"
	"github.com/charmbracelet/lipgloss"
	"github.com/goreleaser/goreleaser/internal/middleware"
	"github.com/goreleaser/goreleaser/pkg/context"
)

var (
	bold  = lipgloss.NewStyle().Bold(true)
	faint = lipgloss.NewStyle().Italic(true).Faint(true)
)

// Log pretty prints the given action and its title.
func Log(title string, next middleware.Action) middleware.Action {
	return func(ctx *context.Context) error {
		start := time.Now()
		defer func() {
			if took := time.Since(start).Round(time.Second); took > 0 {
				log.Info(faint.Render(fmt.Sprintf("took: %s", took)))
			}
			log.ResetPadding()
		}()
		log.Infof(bold.Render(title))
		log.IncreasePadding()
		return next(ctx)
	}
}

// PadLog pretty prints the given action and its title with an increased padding.
func PadLog(title string, next middleware.Action) middleware.Action {
	return func(ctx *context.Context) error {
		defer log.ResetPadding()
		log.IncreasePadding()
		log.Infof(bold.Render(title))
		log.IncreasePadding()
		return next(ctx)
	}
}
<|endoftext|>


<commit_before>// +build kube

/*
   Copyright 2020 Docker Compose CLI authors

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package client

import (
	"context"
	"fmt"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/cli-runtime/pkg/genericclioptions"
	"k8s.io/client-go/kubernetes"

	"github.com/docker/compose-cli/api/compose"
)

// KubeClient API to access kube objects
type KubeClient struct {
	client *kubernetes.Clientset
}

// NewKubeClient new kubernetes client
func NewKubeClient(config genericclioptions.RESTClientGetter) (*KubeClient, error) {
	restConfig, err := config.ToRESTConfig()
	if err != nil {
		return nil, err
	}

	clientset, err := kubernetes.NewForConfig(restConfig)
	if err != nil {
		return nil, err
	}
	return &KubeClient{
		client: clientset,
	}, nil
}

// GetContainers get containers for a given compose project
func (kc KubeClient) GetContainers(ctx context.Context, projectName string, all bool) ([]compose.ContainerSummary, error) {
	fieldSelector := ""
	if !all {
		fieldSelector = "status.phase=Running"
	}

	pods, err := kc.client.CoreV1().Pods("").List(ctx, metav1.ListOptions{
		LabelSelector: fmt.Sprintf("%s=%s", compose.ProjectTag, projectName),
		FieldSelector: fieldSelector,
	})
	if err != nil {
		return nil, err
	}
	result := []compose.ContainerSummary{}
	for _, pod := range pods.Items {
		result = append(result, podToContainerSummary(pod))
	}

	return result, nil
}

func podToContainerSummary(pod v1.Pod) compose.ContainerSummary {
	return compose.ContainerSummary{
		ID:      pod.GetObjectMeta().GetName(),
		Name:    pod.GetObjectMeta().GetName(),
		Service: pod.GetObjectMeta().GetLabels()[compose.ServiceTag],
		State:   string(pod.Status.Phase),
		Project: pod.GetObjectMeta().GetLabels()[compose.ProjectTag],
	}
}
<commit_msg>Set kube namespace properly<commit_after>// +build kube

/*
   Copyright 2020 Docker Compose CLI authors

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package client

import (
	"context"
	"fmt"

	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/cli-runtime/pkg/genericclioptions"
	"k8s.io/client-go/kubernetes"

	"github.com/docker/compose-cli/api/compose"
)

// KubeClient API to access kube objects
type KubeClient struct {
	client    *kubernetes.Clientset
	namespace string
}

// NewKubeClient new kubernetes client
func NewKubeClient(config genericclioptions.RESTClientGetter) (*KubeClient, error) {
	restConfig, err := config.ToRESTConfig()
	if err != nil {
		return nil, err
	}

	clientset, err := kubernetes.NewForConfig(restConfig)
	if err != nil {
		return nil, err
	}

	namespace, _, err := config.ToRawKubeConfigLoader().Namespace()
	if err != nil {
		return nil, err
	}

	return &KubeClient{
		client:    clientset,
		namespace: namespace,
	}, nil
}

// GetContainers get containers for a given compose project
func (kc KubeClient) GetContainers(ctx context.Context, projectName string, all bool) ([]compose.ContainerSummary, error) {
	fieldSelector := ""
	if !all {
		fieldSelector = "status.phase=Running"
	}

	pods, err := kc.client.CoreV1().Pods(kc.namespace).List(ctx, metav1.ListOptions{
		LabelSelector: fmt.Sprintf("%s=%s", compose.ProjectTag, projectName),
		FieldSelector: fieldSelector,
	})
	if err != nil {
		return nil, err
	}
	result := []compose.ContainerSummary{}
	for _, pod := range pods.Items {
		result = append(result, podToContainerSummary(pod))
	}

	return result, nil
}

func podToContainerSummary(pod v1.Pod) compose.ContainerSummary {
	return compose.ContainerSummary{
		ID:      pod.GetObjectMeta().GetName(),
		Name:    pod.GetObjectMeta().GetName(),
		Service: pod.GetObjectMeta().GetLabels()[compose.ServiceTag],
		State:   string(pod.Status.Phase),
		Project: pod.GetObjectMeta().GetLabels()[compose.ProjectTag],
	}
}
<|endoftext|>


<commit_before>// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

// Timer Operations

package gtimer_test

import (
	"github.com/gogf/gf/os/glog"
	"testing"
	"time"

	"github.com/gogf/gf/container/garray"
	"github.com/gogf/gf/os/gtimer"
	"github.com/gogf/gf/test/gtest"
)

func New() *gtimer.Timer {
	return gtimer.New(10, 10*time.Millisecond)
}

func TestTimer_Add_Close(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		timer := New()
		array := garray.New(true)
		//fmt.Println("start", time.Now())
		timer.Add(200*time.Millisecond, func() {
			//fmt.Println("entry1", time.Now())
			array.Append(1)
		})
		timer.Add(200*time.Millisecond, func() {
			//fmt.Println("entry2", time.Now())
			array.Append(1)
		})
		timer.Add(400*time.Millisecond, func() {
			//fmt.Println("entry3", time.Now())
			array.Append(1)
		})
		time.Sleep(250 * time.Millisecond)
		t.Assert(array.Len(), 2)
		time.Sleep(250 * time.Millisecond)
		t.Assert(array.Len(), 5)
		timer.Close()
		time.Sleep(250 * time.Millisecond)
		fixedLength := array.Len()
		time.Sleep(250 * time.Millisecond)
		t.Assert(array.Len(), fixedLength)
	})
}

func TestTimer_Start_Stop_Close(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		timer := New()
		array := garray.New(true)
		timer.Add(200*time.Millisecond, func() {
			//glog.Println("add...")
			array.Append(1)
		})
		t.Assert(array.Len(), 0)
		time.Sleep(300 * time.Millisecond)
		t.Assert(array.Len(), 1)
		timer.Stop()
		time.Sleep(1000 * time.Millisecond)
		t.Assert(array.Len(), 1)
		timer.Start()
		time.Sleep(200 * time.Millisecond)
		t.Assert(array.Len(), 2)
		timer.Close()
		time.Sleep(1000 * time.Millisecond)
		t.Assert(array.Len(), 2)
	})
}

func TestTimer_Reset(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		array := garray.New(true)
		glog.Printf("start time:%d", time.Now().Unix())
		singleton := timer.AddSingleton(2*time.Second, func() {
			timestamp := time.Now().Unix()
			glog.Println(timestamp)
			array.Append(timestamp)
		})
		time.Sleep(5 * time.Second)
		glog.Printf("reset time:%d", time.Now().Unix())
		singleton.Reset()
		time.Sleep(10 * time.Second)
		t.Assert(array.Len(), 6)
	})
}

func TestTimer_AddSingleton(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		timer := New()
		array := garray.New(true)
		timer.AddSingleton(200*time.Millisecond, func() {
			array.Append(1)
			time.Sleep(10 * time.Second)
		})
		time.Sleep(250 * time.Millisecond)
		t.Assert(array.Len(), 1)

		time.Sleep(500 * time.Millisecond)
		t.Assert(array.Len(), 1)
	})
}

func TestTimer_AddOnce(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		timer := New()
		array := garray.New(true)
		timer.AddOnce(200*time.Millisecond, func() {
			array.Append(1)
		})
		timer.AddOnce(200*time.Millisecond, func() {
			array.Append(1)
		})
		time.Sleep(250 * time.Millisecond)
		t.Assert(array.Len(), 2)
		time.Sleep(250 * time.Millisecond)
		t.Assert(array.Len(), 2)
		timer.Close()
		time.Sleep(250 * time.Millisecond)
		fixedLength := array.Len()
		time.Sleep(250 * time.Millisecond)
		t.Assert(array.Len(), fixedLength)
	})
}

func TestTimer_AddTimes(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		timer := New()
		array := garray.New(true)
		timer.AddTimes(200*time.Millisecond, 2, func() {
			array.Append(1)
		})
		time.Sleep(1000 * time.Millisecond)
		t.Assert(array.Len(), 2)
	})
}

func TestTimer_DelayAdd(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		timer := New()
		array := garray.New(true)
		timer.DelayAdd(200*time.Millisecond, 200*time.Millisecond, func() {
			array.Append(1)
		})
		time.Sleep(250 * time.Millisecond)
		t.Assert(array.Len(), 0)
		time.Sleep(250 * time.Millisecond)
		t.Assert(array.Len(), 1)
	})
}

func TestTimer_DelayAddEntry(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		timer := New()
		array := garray.New(true)
		timer.DelayAddEntry(200*time.Millisecond, 200*time.Millisecond, func() {
			array.Append(1)
		}, false, 100, gtimer.STATUS_READY)
		time.Sleep(250 * time.Millisecond)
		t.Assert(array.Len(), 0)
		time.Sleep(250 * time.Millisecond)
		t.Assert(array.Len(), 1)
	})
}

func TestTimer_DelayAddSingleton(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		timer := New()
		array := garray.New(true)
		timer.DelayAddSingleton(200*time.Millisecond, 200*time.Millisecond, func() {
			array.Append(1)
			time.Sleep(10 * time.Second)
		})
		time.Sleep(250 * time.Millisecond)
		t.Assert(array.Len(), 0)

		time.Sleep(1000 * time.Millisecond)
		t.Assert(array.Len(), 1)
	})
}

func TestTimer_DelayAddOnce(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		timer := New()
		array := garray.New(true)
		timer.DelayAddOnce(200*time.Millisecond, 200*time.Millisecond, func() {
			array.Append(1)
		})
		time.Sleep(250 * time.Millisecond)
		t.Assert(array.Len(), 0)

		time.Sleep(250 * time.Millisecond)
		t.Assert(array.Len(), 1)

		time.Sleep(500 * time.Millisecond)
		t.Assert(array.Len(), 1)
	})
}

func TestTimer_DelayAddTimes(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		timer := New()
		array := garray.New(true)
		timer.DelayAddTimes(200*time.Millisecond, 500*time.Millisecond, 2, func() {
			array.Append(1)
		})
		time.Sleep(200 * time.Millisecond)
		t.Assert(array.Len(), 0)

		time.Sleep(600 * time.Millisecond)
		t.Assert(array.Len(), 1)

		time.Sleep(600 * time.Millisecond)
		t.Assert(array.Len(), 2)

		time.Sleep(1000 * time.Millisecond)
		t.Assert(array.Len(), 2)
	})
}

func TestTimer_AddLessThanInterval(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		timer := gtimer.New(10, 100*time.Millisecond)
		array := garray.New(true)
		timer.Add(20*time.Millisecond, func() {
			array.Append(1)
		})
		time.Sleep(50 * time.Millisecond)
		t.Assert(array.Len(), 0)

		time.Sleep(110 * time.Millisecond)
		t.Assert(array.Len(), 1)

		time.Sleep(110 * time.Millisecond)
		t.Assert(array.Len(), 2)
	})
}

func TestTimer_AddLeveledEntry1(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		timer := New()
		array := garray.New(true)
		//glog.Println("start")
		timer.DelayAdd(1000*time.Millisecond, 1000*time.Millisecond, func() {
			//glog.Println("add")
			array.Append(1)
		})
		time.Sleep(1500 * time.Millisecond)
		t.Assert(array.Len(), 0)
		time.Sleep(1300 * time.Millisecond)
		//glog.Println("check")
		t.Assert(array.Len(), 1)
	})
}

func TestTimer_Exit(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		timer := New()
		array := garray.New(true)
		timer.Add(200*time.Millisecond, func() {
			array.Append(1)
			gtimer.Exit()
		})
		time.Sleep(1000 * time.Millisecond)
		t.Assert(array.Len(), 1)
	})
}
<commit_msg>adjust gtimer.Entry.Reset() Test Code.<commit_after>// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

// Timer Operations

package gtimer_test

import (
	"github.com/gogf/gf/os/glog"
	"testing"
	"time"

	"github.com/gogf/gf/container/garray"
	"github.com/gogf/gf/os/gtimer"
	"github.com/gogf/gf/test/gtest"
)

func New() *gtimer.Timer {
	return gtimer.New(10, 10*time.Millisecond)
}

func TestTimer_Add_Close(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		timer := New()
		array := garray.New(true)
		//fmt.Println("start", time.Now())
		timer.Add(200*time.Millisecond, func() {
			//fmt.Println("entry1", time.Now())
			array.Append(1)
		})
		timer.Add(200*time.Millisecond, func() {
			//fmt.Println("entry2", time.Now())
			array.Append(1)
		})
		timer.Add(400*time.Millisecond, func() {
			//fmt.Println("entry3", time.Now())
			array.Append(1)
		})
		time.Sleep(250 * time.Millisecond)
		t.Assert(array.Len(), 2)
		time.Sleep(250 * time.Millisecond)
		t.Assert(array.Len(), 5)
		timer.Close()
		time.Sleep(250 * time.Millisecond)
		fixedLength := array.Len()
		time.Sleep(250 * time.Millisecond)
		t.Assert(array.Len(), fixedLength)
	})
}

func TestTimer_Start_Stop_Close(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		timer := New()
		array := garray.New(true)
		timer.Add(200*time.Millisecond, func() {
			//glog.Println("add...")
			array.Append(1)
		})
		t.Assert(array.Len(), 0)
		time.Sleep(300 * time.Millisecond)
		t.Assert(array.Len(), 1)
		timer.Stop()
		time.Sleep(1000 * time.Millisecond)
		t.Assert(array.Len(), 1)
		timer.Start()
		time.Sleep(200 * time.Millisecond)
		t.Assert(array.Len(), 2)
		timer.Close()
		time.Sleep(1000 * time.Millisecond)
		t.Assert(array.Len(), 2)
	})
}

func TestTimer_Reset(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		timer := New()
		array := garray.New(true)
		glog.Printf("start time:%d", time.Now().Unix())
		singleton := timer.AddSingleton(2*time.Second, func() {
			timestamp := time.Now().Unix()
			glog.Println(timestamp)
			array.Append(timestamp)
		})
		time.Sleep(5 * time.Second)
		glog.Printf("reset time:%d", time.Now().Unix())
		singleton.Reset()
		time.Sleep(10 * time.Second)
		t.Assert(array.Len(), 6)
	})
}

func TestTimer_AddSingleton(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		timer := New()
		array := garray.New(true)
		timer.AddSingleton(200*time.Millisecond, func() {
			array.Append(1)
			time.Sleep(10 * time.Second)
		})
		time.Sleep(250 * time.Millisecond)
		t.Assert(array.Len(), 1)

		time.Sleep(500 * time.Millisecond)
		t.Assert(array.Len(), 1)
	})
}

func TestTimer_AddOnce(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		timer := New()
		array := garray.New(true)
		timer.AddOnce(200*time.Millisecond, func() {
			array.Append(1)
		})
		timer.AddOnce(200*time.Millisecond, func() {
			array.Append(1)
		})
		time.Sleep(250 * time.Millisecond)
		t.Assert(array.Len(), 2)
		time.Sleep(250 * time.Millisecond)
		t.Assert(array.Len(), 2)
		timer.Close()
		time.Sleep(250 * time.Millisecond)
		fixedLength := array.Len()
		time.Sleep(250 * time.Millisecond)
		t.Assert(array.Len(), fixedLength)
	})
}

func TestTimer_AddTimes(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		timer := New()
		array := garray.New(true)
		timer.AddTimes(200*time.Millisecond, 2, func() {
			array.Append(1)
		})
		time.Sleep(1000 * time.Millisecond)
		t.Assert(array.Len(), 2)
	})
}

func TestTimer_DelayAdd(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		timer := New()
		array := garray.New(true)
		timer.DelayAdd(200*time.Millisecond, 200*time.Millisecond, func() {
			array.Append(1)
		})
		time.Sleep(250 * time.Millisecond)
		t.Assert(array.Len(), 0)
		time.Sleep(250 * time.Millisecond)
		t.Assert(array.Len(), 1)
	})
}

func TestTimer_DelayAddEntry(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		timer := New()
		array := garray.New(true)
		timer.DelayAddEntry(200*time.Millisecond, 200*time.Millisecond, func() {
			array.Append(1)
		}, false, 100, gtimer.STATUS_READY)
		time.Sleep(250 * time.Millisecond)
		t.Assert(array.Len(), 0)
		time.Sleep(250 * time.Millisecond)
		t.Assert(array.Len(), 1)
	})
}

func TestTimer_DelayAddSingleton(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		timer := New()
		array := garray.New(true)
		timer.DelayAddSingleton(200*time.Millisecond, 200*time.Millisecond, func() {
			array.Append(1)
			time.Sleep(10 * time.Second)
		})
		time.Sleep(250 * time.Millisecond)
		t.Assert(array.Len(), 0)

		time.Sleep(1000 * time.Millisecond)
		t.Assert(array.Len(), 1)
	})
}

func TestTimer_DelayAddOnce(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		timer := New()
		array := garray.New(true)
		timer.DelayAddOnce(200*time.Millisecond, 200*time.Millisecond, func() {
			array.Append(1)
		})
		time.Sleep(250 * time.Millisecond)
		t.Assert(array.Len(), 0)

		time.Sleep(250 * time.Millisecond)
		t.Assert(array.Len(), 1)

		time.Sleep(500 * time.Millisecond)
		t.Assert(array.Len(), 1)
	})
}

func TestTimer_DelayAddTimes(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		timer := New()
		array := garray.New(true)
		timer.DelayAddTimes(200*time.Millisecond, 500*time.Millisecond, 2, func() {
			array.Append(1)
		})
		time.Sleep(200 * time.Millisecond)
		t.Assert(array.Len(), 0)

		time.Sleep(600 * time.Millisecond)
		t.Assert(array.Len(), 1)

		time.Sleep(600 * time.Millisecond)
		t.Assert(array.Len(), 2)

		time.Sleep(1000 * time.Millisecond)
		t.Assert(array.Len(), 2)
	})
}

func TestTimer_AddLessThanInterval(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		timer := gtimer.New(10, 100*time.Millisecond)
		array := garray.New(true)
		timer.Add(20*time.Millisecond, func() {
			array.Append(1)
		})
		time.Sleep(50 * time.Millisecond)
		t.Assert(array.Len(), 0)

		time.Sleep(110 * time.Millisecond)
		t.Assert(array.Len(), 1)

		time.Sleep(110 * time.Millisecond)
		t.Assert(array.Len(), 2)
	})
}

func TestTimer_AddLeveledEntry1(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		timer := New()
		array := garray.New(true)
		//glog.Println("start")
		timer.DelayAdd(1000*time.Millisecond, 1000*time.Millisecond, func() {
			//glog.Println("add")
			array.Append(1)
		})
		time.Sleep(1500 * time.Millisecond)
		t.Assert(array.Len(), 0)
		time.Sleep(1300 * time.Millisecond)
		//glog.Println("check")
		t.Assert(array.Len(), 1)
	})
}

func TestTimer_Exit(t *testing.T) {
	gtest.C(t, func(t *gtest.T) {
		timer := New()
		array := garray.New(true)
		timer.Add(200*time.Millisecond, func() {
			array.Append(1)
			gtimer.Exit()
		})
		time.Sleep(1000 * time.Millisecond)
		t.Assert(array.Len(), 1)
	})
}
<|endoftext|>


<commit_before>package arn

import (
	"errors"
	"os"
	"os/exec"
	"path"
	"sort"
	"strings"

	"github.com/aerogo/nano"
	"github.com/animenotifier/arn/autocorrect"
	"github.com/fatih/color"
)

// SoundTrack ...
type SoundTrack struct {
	ID      string           `json:"id"`
	Title   SoundTrackTitle  `json:"title" editable:"true"`
	Media   []*ExternalMedia `json:"media" editable:"true"`
	Links   []*Link          `json:"links" editable:"true"`
	Lyrics  SoundTrackLyrics `json:"lyrics" editable:"true"`
	Tags    []string         `json:"tags" editable:"true" tooltip:"

anime:ID to connect it with anime

opening for openings

ending for endings

cover for covers

remix for remixes

"`
	IsDraft bool             `json:"isDraft" editable:"true"`
	File    string           `json:"file"`

	HasCreator
	HasEditor
	HasLikes
}

// Link returns the permalink for the track.
func (track *SoundTrack) Link() string {
	return "/soundtrack/" + track.ID
}

// MediaByService ...
func (track *SoundTrack) MediaByService(service string) []*ExternalMedia {
	filtered := []*ExternalMedia{}

	for _, media := range track.Media {
		if media.Service == service {
			filtered = append(filtered, media)
		}
	}

	return filtered
}

// HasTag returns true if it contains the given tag.
func (track *SoundTrack) HasTag(search string) bool {
	for _, tag := range track.Tags {
		if tag == search {
			return true
		}
	}

	return false
}

// Anime fetches all tagged anime of the sound track.
func (track *SoundTrack) Anime() []*Anime {
	var animeList []*Anime

	for _, tag := range track.Tags {
		if strings.HasPrefix(tag, "anime:") {
			animeID := strings.TrimPrefix(tag, "anime:")
			anime, err := GetAnime(animeID)

			if err != nil {
				color.Red("Error fetching anime: %v", err)
				continue
			}

			animeList = append(animeList, anime)
		}
	}

	return animeList
}

// OsuBeatmaps returns all osu beatmap IDs of the sound track.
func (track *SoundTrack) OsuBeatmaps() []string {
	return FilterIDTags(track.Tags, "osu-beatmap")
}

// EtternaBeatmaps returns all Etterna song IDs of the sound track.
func (track *SoundTrack) EtternaBeatmaps() []string {
	return FilterIDTags(track.Tags, "etterna")
}

// MainAnime ...
func (track *SoundTrack) MainAnime() *Anime {
	allAnime := track.Anime()

	if len(allAnime) == 0 {
		return nil
	}

	return allAnime[0]
}

// Creator returns the user who created this track.
func (track *SoundTrack) Creator() *User {
	user, _ := GetUser(track.CreatedBy)
	return user
}

// EditedByUser returns the user who edited this track last.
func (track *SoundTrack) EditedByUser() *User {
	user, _ := GetUser(track.EditedBy)
	return user
}

// OnLike is called when the soundtrack receives a like.
func (track *SoundTrack) OnLike(likedBy *User) {
	if likedBy.ID == track.CreatedBy {
		return
	}

	if !track.Creator().Settings().Notification.SoundTrackLikes {
		return
	}

	go func() {
		track.Creator().SendNotification(&PushNotification{
			Title:   likedBy.Nick + " liked your soundtrack " + track.Title.ByUser(track.Creator()),
			Message: likedBy.Nick + " liked your soundtrack " + track.Title.ByUser(track.Creator()) + ".",
			Icon:    "https:" + likedBy.AvatarLink("large"),
			Link:    "https://notify.moe" + likedBy.Link(),
			Type:    NotificationTypeLike,
		})
	}()
}

// Publish ...
func (track *SoundTrack) Publish() error {
	// No draft
	if !track.IsDraft {
		return errors.New("Not a draft")
	}

	// No media added
	if len(track.Media) == 0 {
		return errors.New("No media specified (at least 1 media source is required)")
	}

	animeFound := false

	for _, tag := range track.Tags {
		tag = autocorrect.Tag(tag)

		if strings.HasPrefix(tag, "anime:") {
			animeID := strings.TrimPrefix(tag, "anime:")
			_, err := GetAnime(animeID)

			if err != nil {
				return errors.New("Invalid anime ID")
			}

			animeFound = true
		}
	}

	// No anime found
	if !animeFound {
		return errors.New("Need to specify at least one anime")
	}

	// No tags
	if len(track.Tags) < 1 {
		return errors.New("Need to specify at least one tag")
	}

	draftIndex, err := GetDraftIndex(track.CreatedBy)

	if err != nil {
		return err
	}

	if draftIndex.SoundTrackID == "" {
		return errors.New("Soundtrack draft doesn't exist in the user draft index")
	}

	track.IsDraft = false
	draftIndex.SoundTrackID = ""
	draftIndex.Save()

	// Start download in the background
	go func() {
		err := track.Download()

		if err == nil {
			track.Save()
		}
	}()

	return nil
}

// Unpublish ...
func (track *SoundTrack) Unpublish() error {
	draftIndex, err := GetDraftIndex(track.CreatedBy)

	if err != nil {
		return err
	}

	if draftIndex.SoundTrackID != "" {
		return errors.New("You still have an unfinished draft")
	}

	track.IsDraft = true
	draftIndex.SoundTrackID = track.ID
	draftIndex.Save()
	return nil
}

// Download downloads the track.
func (track *SoundTrack) Download() error {
	if track.IsDraft {
		return errors.New("Track is a draft")
	}

	youtubeVideos := track.MediaByService("Youtube")

	if len(youtubeVideos) == 0 {
		return errors.New("No Youtube ID")
	}

	youtubeID := youtubeVideos[0].ServiceID

	// Check for existing file
	if track.File != "" {
		stat, err := os.Stat(path.Join(Root, "audio", track.File))

		if err == nil && !stat.IsDir() && stat.Size() > 0 {
			return errors.New("Already downloaded")
		}
	}

	audioDirectory := path.Join(Root, "audio")
	baseName := track.ID + "|" + youtubeID

	// Check if it exists on the file system
	fullPath := FindFileWithExtension(baseName, audioDirectory, []string{
		".opus",
		".webm",
		".ogg",
		".m4a",
		".mp3",
		".flac",
		".wav",
	})

	// In case we added the file but didn't register it in database
	if fullPath != "" {
		extension := path.Ext(fullPath)
		track.File = baseName + extension
		return nil
	}

	filePath := path.Join(audioDirectory, baseName)

	// Download
	cmd := exec.Command("youtube-dl", "--extract-audio", "--audio-quality", "0", "--output", filePath+".%(ext)s", youtubeID)
	err := cmd.Start()

	if err != nil {
		return err
	}

	err = cmd.Wait()

	if err != nil {
		return err
	}

	// Find downloaded file
	fullPath = FindFileWithExtension(baseName, audioDirectory, []string{
		".opus",
		".webm",
		".ogg",
		".m4a",
		".mp3",
		".flac",
		".wav",
	})

	extension := path.Ext(fullPath)
	track.File = baseName + extension
	return nil
}

// String implements the default string serialization.
func (track *SoundTrack) String() string {
	return track.Title.ByUser(nil)
}

// SortSoundTracksLatestFirst ...
func SortSoundTracksLatestFirst(tracks []*SoundTrack) {
	sort.Slice(tracks, func(i, j int) bool {
		return tracks[i].Created > tracks[j].Created
	})
}

// SortSoundTracksPopularFirst ...
func SortSoundTracksPopularFirst(tracks []*SoundTrack) {
	sort.Slice(tracks, func(i, j int) bool {
		aLikes := len(tracks[i].Likes)
		bLikes := len(tracks[j].Likes)

		if aLikes == bLikes {
			return tracks[i].Created > tracks[j].Created
		}

		return aLikes > bLikes
	})
}

// GetSoundTrack ...
func GetSoundTrack(id string) (*SoundTrack, error) {
	track, err := DB.Get("SoundTrack", id)

	if err != nil {
		return nil, err
	}

	return track.(*SoundTrack), nil
}

// StreamSoundTracks returns a stream of all soundtracks.
func StreamSoundTracks() chan *SoundTrack {
	channel := make(chan *SoundTrack, nano.ChannelBufferSize)

	go func() {
		for obj := range DB.All("SoundTrack") {
			channel <- obj.(*SoundTrack)
		}

		close(channel)
	}()

	return channel
}

// AllSoundTracks ...
func AllSoundTracks() []*SoundTrack {
	var all []*SoundTrack

	for obj := range StreamSoundTracks() {
		all = append(all, obj)
	}

	return all
}

// FilterSoundTracks filters all soundtracks by a custom function.
func FilterSoundTracks(filter func(*SoundTrack) bool) []*SoundTrack {
	var filtered []*SoundTrack

	for obj := range StreamSoundTracks() {
		if filter(obj) {
			filtered = append(filtered, obj)
		}
	}

	return filtered
}
<commit_msg>Added soundtrack tags explanation<commit_after>package arn

import (
	"errors"
	"os"
	"os/exec"
	"path"
	"sort"
	"strings"

	"github.com/aerogo/nano"
	"github.com/animenotifier/arn/autocorrect"
	"github.com/fatih/color"
)

// SoundTrack ...
type SoundTrack struct {
	ID      string           `json:"id"`
	Title   SoundTrackTitle  `json:"title" editable:"true"`
	Media   []*ExternalMedia `json:"media" editable:"true"`
	Links   []*Link          `json:"links" editable:"true"`
	Lyrics  SoundTrackLyrics `json:"lyrics" editable:"true"`
	Tags    []string         `json:"tags" editable:"true" tooltip:"

anime:ID to connect it with anime (e.g. anime:yF1RhKiiR)

opening for openings

ending for endings

op:NUMBER or ed:NUMBER if it has more than one OP/ED (e.g. op:2 or ed:3)

cover for covers

remix for remixes

male or female

vocal, orchestral or instrumental

"`
	IsDraft bool             `json:"isDraft" editable:"true"`
	File    string           `json:"file"`

	HasCreator
	HasEditor
	HasLikes
}

// Link returns the permalink for the track.
func (track *SoundTrack) Link() string {
	return "/soundtrack/" + track.ID
}

// MediaByService ...
func (track *SoundTrack) MediaByService(service string) []*ExternalMedia {
	filtered := []*ExternalMedia{}

	for _, media := range track.Media {
		if media.Service == service {
			filtered = append(filtered, media)
		}
	}

	return filtered
}

// HasTag returns true if it contains the given tag.
func (track *SoundTrack) HasTag(search string) bool {
	for _, tag := range track.Tags {
		if tag == search {
			return true
		}
	}

	return false
}

// Anime fetches all tagged anime of the sound track.
func (track *SoundTrack) Anime() []*Anime {
	var animeList []*Anime

	for _, tag := range track.Tags {
		if strings.HasPrefix(tag, "anime:") {
			animeID := strings.TrimPrefix(tag, "anime:")
			anime, err := GetAnime(animeID)

			if err != nil {
				color.Red("Error fetching anime: %v", err)
				continue
			}

			animeList = append(animeList, anime)
		}
	}

	return animeList
}

// OsuBeatmaps returns all osu beatmap IDs of the sound track.
func (track *SoundTrack) OsuBeatmaps() []string {
	return FilterIDTags(track.Tags, "osu-beatmap")
}

// EtternaBeatmaps returns all Etterna song IDs of the sound track.
func (track *SoundTrack) EtternaBeatmaps() []string {
	return FilterIDTags(track.Tags, "etterna")
}

// MainAnime ...
func (track *SoundTrack) MainAnime() *Anime {
	allAnime := track.Anime()

	if len(allAnime) == 0 {
		return nil
	}

	return allAnime[0]
}

// Creator returns the user who created this track.
func (track *SoundTrack) Creator() *User {
	user, _ := GetUser(track.CreatedBy)
	return user
}

// EditedByUser returns the user who edited this track last.
func (track *SoundTrack) EditedByUser() *User {
	user, _ := GetUser(track.EditedBy)
	return user
}

// OnLike is called when the soundtrack receives a like.
func (track *SoundTrack) OnLike(likedBy *User) {
	if likedBy.ID == track.CreatedBy {
		return
	}

	if !track.Creator().Settings().Notification.SoundTrackLikes {
		return
	}

	go func() {
		track.Creator().SendNotification(&PushNotification{
			Title:   likedBy.Nick + " liked your soundtrack " + track.Title.ByUser(track.Creator()),
			Message: likedBy.Nick + " liked your soundtrack " + track.Title.ByUser(track.Creator()) + ".",
			Icon:    "https:" + likedBy.AvatarLink("large"),
			Link:    "https://notify.moe" + likedBy.Link(),
			Type:    NotificationTypeLike,
		})
	}()
}

// Publish ...
func (track *SoundTrack) Publish() error {
	// No draft
	if !track.IsDraft {
		return errors.New("Not a draft")
	}

	// No media added
	if len(track.Media) == 0 {
		return errors.New("No media specified (at least 1 media source is required)")
	}

	animeFound := false

	for _, tag := range track.Tags {
		tag = autocorrect.Tag(tag)

		if strings.HasPrefix(tag, "anime:") {
			animeID := strings.TrimPrefix(tag, "anime:")
			_, err := GetAnime(animeID)

			if err != nil {
				return errors.New("Invalid anime ID")
			}

			animeFound = true
		}
	}

	// No anime found
	if !animeFound {
		return errors.New("Need to specify at least one anime")
	}

	// No tags
	if len(track.Tags) < 1 {
		return errors.New("Need to specify at least one tag")
	}

	draftIndex, err := GetDraftIndex(track.CreatedBy)

	if err != nil {
		return err
	}

	if draftIndex.SoundTrackID == "" {
		return errors.New("Soundtrack draft doesn't exist in the user draft index")
	}

	track.IsDraft = false
	draftIndex.SoundTrackID = ""
	draftIndex.Save()

	// Start download in the background
	go func() {
		err := track.Download()

		if err == nil {
			track.Save()
		}
	}()

	return nil
}

// Unpublish ...
func (track *SoundTrack) Unpublish() error {
	draftIndex, err := GetDraftIndex(track.CreatedBy)

	if err != nil {
		return err
	}

	if draftIndex.SoundTrackID != "" {
		return errors.New("You still have an unfinished draft")
	}

	track.IsDraft = true
	draftIndex.SoundTrackID = track.ID
	draftIndex.Save()
	return nil
}

// Download downloads the track.
func (track *SoundTrack) Download() error {
	if track.IsDraft {
		return errors.New("Track is a draft")
	}

	youtubeVideos := track.MediaByService("Youtube")

	if len(youtubeVideos) == 0 {
		return errors.New("No Youtube ID")
	}

	youtubeID := youtubeVideos[0].ServiceID

	// Check for existing file
	if track.File != "" {
		stat, err := os.Stat(path.Join(Root, "audio", track.File))

		if err == nil && !stat.IsDir() && stat.Size() > 0 {
			return errors.New("Already downloaded")
		}
	}

	audioDirectory := path.Join(Root, "audio")
	baseName := track.ID + "|" + youtubeID

	// Check if it exists on the file system
	fullPath := FindFileWithExtension(baseName, audioDirectory, []string{
		".opus",
		".webm",
		".ogg",
		".m4a",
		".mp3",
		".flac",
		".wav",
	})

	// In case we added the file but didn't register it in database
	if fullPath != "" {
		extension := path.Ext(fullPath)
		track.File = baseName + extension
		return nil
	}

	filePath := path.Join(audioDirectory, baseName)

	// Download
	cmd := exec.Command("youtube-dl", "--extract-audio", "--audio-quality", "0", "--output", filePath+".%(ext)s", youtubeID)
	err := cmd.Start()

	if err != nil {
		return err
	}

	err = cmd.Wait()

	if err != nil {
		return err
	}

	// Find downloaded file
	fullPath = FindFileWithExtension(baseName, audioDirectory, []string{
		".opus",
		".webm",
		".ogg",
		".m4a",
		".mp3",
		".flac",
		".wav",
	})

	extension := path.Ext(fullPath)
	track.File = baseName + extension
	return nil
}

// String implements the default string serialization.
func (track *SoundTrack) String() string {
	return track.Title.ByUser(nil)
}

// SortSoundTracksLatestFirst ...
func SortSoundTracksLatestFirst(tracks []*SoundTrack) {
	sort.Slice(tracks, func(i, j int) bool {
		return tracks[i].Created > tracks[j].Created
	})
}

// SortSoundTracksPopularFirst ...
func SortSoundTracksPopularFirst(tracks []*SoundTrack) {
	sort.Slice(tracks, func(i, j int) bool {
		aLikes := len(tracks[i].Likes)
		bLikes := len(tracks[j].Likes)

		if aLikes == bLikes {
			return tracks[i].Created > tracks[j].Created
		}

		return aLikes > bLikes
	})
}

// GetSoundTrack ...
func GetSoundTrack(id string) (*SoundTrack, error) {
	track, err := DB.Get("SoundTrack", id)

	if err != nil {
		return nil, err
	}

	return track.(*SoundTrack), nil
}

// StreamSoundTracks returns a stream of all soundtracks.
func StreamSoundTracks() chan *SoundTrack {
	channel := make(chan *SoundTrack, nano.ChannelBufferSize)

	go func() {
		for obj := range DB.All("SoundTrack") {
			channel <- obj.(*SoundTrack)
		}

		close(channel)
	}()

	return channel
}

// AllSoundTracks ...
func AllSoundTracks() []*SoundTrack {
	var all []*SoundTrack

	for obj := range StreamSoundTracks() {
		all = append(all, obj)
	}

	return all
}

// FilterSoundTracks filters all soundtracks by a custom function.
func FilterSoundTracks(filter func(*SoundTrack) bool) []*SoundTrack {
	var filtered []*SoundTrack

	for obj := range StreamSoundTracks() {
		if filter(obj) {
			filtered = append(filtered, obj)
		}
	}

	return filtered
}
<|endoftext|>


<commit_before>package needle

import (
	"fmt"
	"github.com/chrislusf/seaweedfs/weed/storage/backend"
	"io"
	"os"
	"testing"

	"github.com/chrislusf/seaweedfs/weed/storage/types"
)

func TestPageRead(t *testing.T) {
	baseFileName := "43"
	offset := int64(8)
	size := types.Size(1153890) // actual file size 1153862

	datFile, err := os.OpenFile(baseFileName+".dat", os.O_RDONLY, 0644)
	if err != nil {
		t.Fatalf("Open Volume Data File [ERROR]: %v", err)
	}
	datBackend := backend.NewDiskFile(datFile)
	defer datBackend.Close()
	{
		n := new(Needle)

		bytes, err := ReadNeedleBlob(datBackend, offset, size, Version3)
		if err != nil {
			t.Fatalf("readNeedleBlob: %v", err)
		}
		if err = n.ReadBytes(bytes, offset, size, Version3); err != nil {
			t.Fatalf("readNeedleBlob: %v", err)
		}

		fmt.Printf("bytes len %d\n", len(bytes))
		fmt.Printf("name %s size %d\n", n.Name, n.Size)

		fmt.Printf("id %d\n", n.Id)
		fmt.Printf("DataSize %d\n", n.DataSize)
		fmt.Printf("Flags %v\n", n.Flags)
		fmt.Printf("NameSize %d\n", n.NameSize)
		fmt.Printf("MimeSize %d\n", n.MimeSize)
		fmt.Printf("PairsSize %d\n", n.PairsSize)
		fmt.Printf("LastModified %d\n", n.LastModified)
		fmt.Printf("AppendAtNs %d\n", n.AppendAtNs)
		fmt.Printf("Checksum %d\n", n.Checksum)
	}

	{
		n, bytes, bodyLength, err := ReadNeedleHeader(datBackend, Version3, offset)
		if err != nil {
			t.Fatalf("ReadNeedleHeader: %v", err)
		}
		fmt.Printf("bytes len %d\n", len(bytes))
		fmt.Printf("name %s size %d bodyLength:%d\n", n.Name, n.Size, bodyLength)
	}

	{
		n := new(Needle)
		err := n.ReadNeedleMeta(datBackend, offset, size, Version3)
		if err != nil {
			t.Fatalf("ReadNeedleHeader: %v", err)
		}
		fmt.Printf("name %s size %d\n", n.Name, n.Size)
		fmt.Printf("id %d\n", n.Id)
		fmt.Printf("DataSize %d\n", n.DataSize)
		fmt.Printf("Flags %v\n", n.Flags)
		fmt.Printf("NameSize %d\n", n.NameSize)
		fmt.Printf("MimeSize %d\n", n.MimeSize)
		fmt.Printf("PairsSize %d\n", n.PairsSize)
		fmt.Printf("LastModified %d\n", n.LastModified)
		fmt.Printf("AppendAtNs %d\n", n.AppendAtNs)
		fmt.Printf("Checksum %d\n", n.Checksum)
		fmt.Printf("Checksum value %d\n", checksumValue)

		buf := make([]byte, 1024)
		if err = n.ReadNeedleDataInto(datBackend, offset, buf, io.Discard, 0, int64(n.DataSize), checksumValue); err != nil {
			t.Fatalf("ReadNeedleDataInto: %v", err)
		}

	}

}
<commit_msg>fix test<commit_after>package needle

import (
	"fmt"
	"github.com/chrislusf/seaweedfs/weed/storage/backend"
	"io"
	"os"
	"testing"

	"github.com/chrislusf/seaweedfs/weed/storage/types"
)

func TestPageRead(t *testing.T) {
	baseFileName := "43"
	offset := int64(8)
	size := types.Size(1153890) // actual file size 1153862

	datFile, err := os.OpenFile(baseFileName+".dat", os.O_RDONLY, 0644)
	if err != nil {
		t.Fatalf("Open Volume Data File [ERROR]: %v", err)
	}
	datBackend := backend.NewDiskFile(datFile)
	defer datBackend.Close()
	{
		n := new(Needle)

		bytes, err := ReadNeedleBlob(datBackend, offset, size, Version3)
		if err != nil {
			t.Fatalf("readNeedleBlob: %v", err)
		}
		if err = n.ReadBytes(bytes, offset, size, Version3); err != nil {
			t.Fatalf("readNeedleBlob: %v", err)
		}

		fmt.Printf("bytes len %d\n", len(bytes))
		fmt.Printf("name %s size %d\n", n.Name, n.Size)

		fmt.Printf("id %d\n", n.Id)
		fmt.Printf("DataSize %d\n", n.DataSize)
		fmt.Printf("Flags %v\n", n.Flags)
		fmt.Printf("NameSize %d\n", n.NameSize)
		fmt.Printf("MimeSize %d\n", n.MimeSize)
		fmt.Printf("PairsSize %d\n", n.PairsSize)
		fmt.Printf("LastModified %d\n", n.LastModified)
		fmt.Printf("AppendAtNs %d\n", n.AppendAtNs)
		fmt.Printf("Checksum %d\n", n.Checksum)
	}

	{
		n, bytes, bodyLength, err := ReadNeedleHeader(datBackend, Version3, offset)
		if err != nil {
			t.Fatalf("ReadNeedleHeader: %v", err)
		}
		fmt.Printf("bytes len %d\n", len(bytes))
		fmt.Printf("name %s size %d bodyLength:%d\n", n.Name, n.Size, bodyLength)
	}

	{
		n := new(Needle)
		err := n.ReadNeedleMeta(datBackend, offset, size, Version3)
		if err != nil {
			t.Fatalf("ReadNeedleHeader: %v", err)
		}
		fmt.Printf("name %s size %d\n", n.Name, n.Size)
		fmt.Printf("id %d\n", n.Id)
		fmt.Printf("DataSize %d\n", n.DataSize)
		fmt.Printf("Flags %v\n", n.Flags)
		fmt.Printf("NameSize %d\n", n.NameSize)
		fmt.Printf("MimeSize %d\n", n.MimeSize)
		fmt.Printf("PairsSize %d\n", n.PairsSize)
		fmt.Printf("LastModified %d\n", n.LastModified)
		fmt.Printf("AppendAtNs %d\n", n.AppendAtNs)
		fmt.Printf("Checksum %d\n", n.Checksum)

		buf := make([]byte, 1024)
		if err = n.ReadNeedleDataInto(datBackend, offset, buf, io.Discard, 0, int64(n.DataSize)); err != nil {
			t.Fatalf("ReadNeedleDataInto: %v", err)
		}

	}

}
<|endoftext|>


<commit_before>// Copyright 2016 tsuru-client authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package installer

import (
	"encoding/json"
	"fmt"
	"os/exec"

	"github.com/docker/machine/drivers/amazonec2"
	"github.com/docker/machine/drivers/azure"
	"github.com/docker/machine/drivers/digitalocean"
	"github.com/docker/machine/drivers/exoscale"
	"github.com/docker/machine/drivers/generic"
	"github.com/docker/machine/drivers/google"
	"github.com/docker/machine/drivers/hyperv"
	"github.com/docker/machine/drivers/none"
	"github.com/docker/machine/drivers/openstack"
	"github.com/docker/machine/drivers/rackspace"
	"github.com/docker/machine/drivers/softlayer"
	"github.com/docker/machine/drivers/virtualbox"
	"github.com/docker/machine/drivers/vmwarefusion"
	"github.com/docker/machine/drivers/vmwarevcloudair"
	"github.com/docker/machine/drivers/vmwarevsphere"
	"github.com/docker/machine/libmachine"
	"github.com/docker/machine/libmachine/drivers"
	"github.com/docker/machine/libmachine/drivers/plugin"
	"github.com/docker/machine/libmachine/drivers/rpc"
	"github.com/docker/machine/libmachine/host"
	"github.com/fsouza/go-dockerclient"
	"github.com/tsuru/config"
)

var (
	dockerHTTPPort  = 2375
	dockerHTTPSPort = 2376
)

type Machine struct {
	IP      string
	Config  map[string]string
	TLS     bool
	Address string
}

func (m *Machine) dockerClient() (*docker.Client, error) {
	if m.TLS {
		return docker.NewTLSClient(m.Address, "cert.pem", "key.pem", "ca.pem")
	}
	return docker.NewClient(m.Address)
}

type DockerMachine struct {
	driverOpts map[string]interface{}
	rawDriver  []byte
	driverName string
	storePath  string
	certsPath  string
	tlsSupport bool
	Name       string
}

func NewDockerMachine() (*DockerMachine, error) {
	driverName, err := config.GetString("driver:name")
	if err != nil {
		return nil, err
	}
	driverOpts := make(map[string]interface{})
	opts, _ := config.Get("driver:options")
	if opts != nil {
		for k, v := range opts.(map[interface{}]interface{}) {
			switch k := k.(type) {
			case string:
				driverOpts[k] = v
			}
		}
	}
	storePath := "/tmp/automatic"
	certsPath := "/tmp/automatic/certs"
	rawDriver, err := json.Marshal(&drivers.BaseDriver{
		MachineName: "tsuru",
		StorePath:   storePath,
	})
	if err != nil {
		return nil, fmt.Errorf("Error creating docker-machine driver: %s", err)
	}

	dm := &DockerMachine{
		driverOpts: driverOpts,
		rawDriver:  rawDriver,
		driverName: driverName,
		storePath:  storePath,
		certsPath:  certsPath,
		tlsSupport: driverName != "virtualbox" && driverName != "none",
		Name:       "tsuru",
	}
	return dm, nil
}

func (d *DockerMachine) CreateMachine() (*Machine, error) {
	client := libmachine.NewClient(d.storePath, d.certsPath)
	defer client.Close()
	host, err := client.NewHost(d.driverName, d.rawDriver)
	if err != nil {
		return nil, err
	}
	configureDriver(host.Driver, d.driverOpts)
	d.configureHost(host)
	err = client.Create(host)
	if err != nil {
		fmt.Printf("Ignoring error on machine creation: %s", err)
	}
	ip, err := host.Driver.GetIP()
	if err != nil {
		return nil, err
	}
	config := map[string]string{}
	m := Machine{
		IP:     ip,
		Config: config,
		TLS:    d.tlsSupport,
	}
	if m.TLS {
		m.Address = fmt.Sprintf("https://%s:%d", m.IP, dockerHTTPSPort)
	} else {
		m.Address = fmt.Sprintf("http://%s:%d", m.IP, dockerHTTPPort)
	}
	return &m, nil
}

func (d *DockerMachine) CreateRegistryCertificate() error {
	client := libmachine.NewClient(d.storePath, d.certsPath)
	defer client.Close()
	host, err := client.Load(d.Name)
	if err != nil {
		return err
	}
	ip, err := host.Driver.GetIP()
	if err != nil {
		return err
	}
	args := []string{
		"-o StrictHostKeyChecking=no",
		"-i",
		fmt.Sprintf("%s/machines/%s/id_rsa", d.storePath, d.Name),
		"-r",
		fmt.Sprintf("%s/", d.certsPath),
		fmt.Sprintf("docker@%s:/home/docker/", ip),
	}
	comm := exec.Command("scp", args...)
	stdout, err := comm.CombinedOutput()
	if err != nil {
		return fmt.Errorf("Command: %s. Error:%s", string(stdout), err.Error())
	}
	registryCAConf := fmt.Sprintf(`
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = %s.localdomain
DNS.2 = %s
IP.1 = %s
	`, d.Name, d.Name, ip)

	certsBasePath := "/home/docker/certs"
	_, err = host.RunSSHCommand(fmt.Sprintf("openssl genrsa -out %s/registry-key.pem 2048", certsBasePath))
	if err != nil {
		return err
	}
	registryCAConfPath := "/home/docker/certs/registry_ca.conf"
	_, err = host.RunSSHCommand(fmt.Sprintf("echo -e '%s' >> %s", registryCAConf, registryCAConfPath))
	if err != nil {
		return err
	}
	_, err = host.RunSSHCommand(fmt.Sprintf("openssl req -new -key %s/registry-key.pem -out %s/registry.csr -subj '/CN=%s.localdomain' -config %s", certsBasePath, certsBasePath, d.Name, registryCAConfPath))
	if err != nil {
		return err
	}
	_, err = host.RunSSHCommand(fmt.Sprintf("openssl x509 -req -in %s/registry.csr -CA '%s/ca.pem' -CAkey '%s/ca-key.pem' -CAcreateserial -out '%s/registry.pem' -days 365 -extensions v3_req -extfile %s", certsBasePath, certsBasePath, certsBasePath, certsBasePath, registryCAConfPath))
	if err != nil {
		return err
	}
	_, err = host.RunSSHCommand(fmt.Sprintf("sudo mkdir -p /var/lib/boot2docker/certs/"))
	if err != nil {
		return err
	}
	_, err = host.RunSSHCommand(fmt.Sprintf("sudo cp %s/ca.pem /var/lib/boot2docker/certs/", certsBasePath))
	return err
}

func configureDriver(driver drivers.Driver, driverOpts map[string]interface{}) error {
	opts := &rpcdriver.RPCFlags{Values: driverOpts}
	for _, c := range driver.GetCreateFlags() {
		_, ok := opts.Values[c.String()]
		if ok == false {
			opts.Values[c.String()] = c.Default()
			if c.Default() == nil {
				opts.Values[c.String()] = false
			}
		}
	}
	if err := driver.SetConfigFromFlags(opts); err != nil {
		return fmt.Errorf("Error setting driver configurations: %s", err)
	}
	return nil
}

func (d *DockerMachine) configureHost(host *host.Host) {
	if d.tlsSupport == false {
		host.HostOptions.EngineOptions.Env = []string{"DOCKER_TLS=no"}
		host.HostOptions.EngineOptions.ArbitraryFlags = []string{fmt.Sprintf("host=tcp://0.0.0.0:%d", dockerHTTPPort)}
	}
}

func (d *DockerMachine) DeleteMachine(m *Machine) error {
	client := libmachine.NewClient(d.storePath, d.certsPath)
	defer client.Close()
	h, err := client.Load("tsuru")
	if err != nil {
		return err
	}
	err = h.Driver.Remove()
	if err != nil {
		return err
	}
	return client.Remove("tsuru")
}

func RunDriver(driverName string) error {
	switch driverName {
	case "amazonec2":
		plugin.RegisterDriver(amazonec2.NewDriver("", ""))
	case "azure":
		plugin.RegisterDriver(azure.NewDriver("", ""))
	case "digitalocean":
		plugin.RegisterDriver(digitalocean.NewDriver("", ""))
	case "exoscale":
		plugin.RegisterDriver(exoscale.NewDriver("", ""))
	case "generic":
		plugin.RegisterDriver(generic.NewDriver("", ""))
	case "google":
		plugin.RegisterDriver(google.NewDriver("", ""))
	case "hyperv":
		plugin.RegisterDriver(hyperv.NewDriver("", ""))
	case "none":
		plugin.RegisterDriver(none.NewDriver("", ""))
	case "openstack":
		plugin.RegisterDriver(openstack.NewDriver("", ""))
	case "rackspace":
		plugin.RegisterDriver(rackspace.NewDriver("", ""))
	case "softlayer":
		plugin.RegisterDriver(softlayer.NewDriver("", ""))
	case "virtualbox":
		plugin.RegisterDriver(virtualbox.NewDriver("", ""))
	case "vmwarefusion":
		plugin.RegisterDriver(vmwarefusion.NewDriver("", ""))
	case "vmwarevcloudair":
		plugin.RegisterDriver(vmwarevcloudair.NewDriver("", ""))
	case "vmwarevsphere":
		plugin.RegisterDriver(vmwarevsphere.NewDriver("", ""))
	default:
		return fmt.Errorf("Unsupported driver: %s\n", driverName)
	}
	return nil
}
<commit_msg>set ca.pem to docker<commit_after>// Copyright 2016 tsuru-client authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package installer

import (
	"encoding/json"
	"fmt"
	"os/exec"
	"time"

	"github.com/docker/machine/drivers/amazonec2"
	"github.com/docker/machine/drivers/azure"
	"github.com/docker/machine/drivers/digitalocean"
	"github.com/docker/machine/drivers/exoscale"
	"github.com/docker/machine/drivers/generic"
	"github.com/docker/machine/drivers/google"
	"github.com/docker/machine/drivers/hyperv"
	"github.com/docker/machine/drivers/none"
	"github.com/docker/machine/drivers/openstack"
	"github.com/docker/machine/drivers/rackspace"
	"github.com/docker/machine/drivers/softlayer"
	"github.com/docker/machine/drivers/virtualbox"
	"github.com/docker/machine/drivers/vmwarefusion"
	"github.com/docker/machine/drivers/vmwarevcloudair"
	"github.com/docker/machine/drivers/vmwarevsphere"
	"github.com/docker/machine/libmachine"
	"github.com/docker/machine/libmachine/drivers"
	"github.com/docker/machine/libmachine/drivers/plugin"
	"github.com/docker/machine/libmachine/drivers/rpc"
	"github.com/docker/machine/libmachine/host"
	"github.com/fsouza/go-dockerclient"
	"github.com/tsuru/config"
)

var (
	dockerHTTPPort  = 2375
	dockerHTTPSPort = 2376
)

type Machine struct {
	IP      string
	Config  map[string]string
	TLS     bool
	Address string
}

func (m *Machine) dockerClient() (*docker.Client, error) {
	if m.TLS {
		return docker.NewTLSClient(m.Address, "cert.pem", "key.pem", "ca.pem")
	}
	return docker.NewClient(m.Address)
}

type DockerMachine struct {
	driverOpts map[string]interface{}
	rawDriver  []byte
	driverName string
	storePath  string
	certsPath  string
	tlsSupport bool
	Name       string
}

func NewDockerMachine() (*DockerMachine, error) {
	driverName, err := config.GetString("driver:name")
	if err != nil {
		return nil, err
	}
	driverOpts := make(map[string]interface{})
	opts, _ := config.Get("driver:options")
	if opts != nil {
		for k, v := range opts.(map[interface{}]interface{}) {
			switch k := k.(type) {
			case string:
				driverOpts[k] = v
			}
		}
	}
	storePath := "/tmp/automatic"
	certsPath := "/tmp/automatic/certs"
	rawDriver, err := json.Marshal(&drivers.BaseDriver{
		MachineName: "tsuru",
		StorePath:   storePath,
	})
	if err != nil {
		return nil, fmt.Errorf("Error creating docker-machine driver: %s", err)
	}

	dm := &DockerMachine{
		driverOpts: driverOpts,
		rawDriver:  rawDriver,
		driverName: driverName,
		storePath:  storePath,
		certsPath:  certsPath,
		tlsSupport: driverName != "virtualbox" && driverName != "none",
		Name:       "tsuru",
	}
	return dm, nil
}

func (d *DockerMachine) CreateMachine() (*Machine, error) {
	client := libmachine.NewClient(d.storePath, d.certsPath)
	defer client.Close()
	host, err := client.NewHost(d.driverName, d.rawDriver)
	if err != nil {
		return nil, err
	}
	configureDriver(host.Driver, d.driverOpts)
	d.configureHost(host)
	err = client.Create(host)
	if err != nil {
		fmt.Printf("Ignoring error on machine creation: %s", err)
	}
	ip, err := host.Driver.GetIP()
	if err != nil {
		return nil, err
	}
	config := map[string]string{}
	m := Machine{
		IP:     ip,
		Config: config,
		TLS:    d.tlsSupport,
	}
	if m.TLS {
		m.Address = fmt.Sprintf("https://%s:%d", m.IP, dockerHTTPSPort)
	} else {
		m.Address = fmt.Sprintf("http://%s:%d", m.IP, dockerHTTPPort)
	}
	return &m, nil
}

func (d *DockerMachine) CreateRegistryCertificate() error {
	client := libmachine.NewClient(d.storePath, d.certsPath)
	defer client.Close()
	host, err := client.Load(d.Name)
	if err != nil {
		return err
	}
	ip, err := host.Driver.GetIP()
	if err != nil {
		return err
	}
	args := []string{
		"-o StrictHostKeyChecking=no",
		"-i",
		fmt.Sprintf("%s/machines/%s/id_rsa", d.storePath, d.Name),
		"-r",
		fmt.Sprintf("%s/", d.certsPath),
		fmt.Sprintf("docker@%s:/home/docker/", ip),
	}
	comm := exec.Command("scp", args...)
	stdout, err := comm.CombinedOutput()
	if err != nil {
		return fmt.Errorf("Command: %s. Error:%s", string(stdout), err.Error())
	}
	registryCAConf := fmt.Sprintf(`
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = %s.localdomain
DNS.2 = %s
IP.1 = %s
	`, d.Name, d.Name, ip)

	certsBasePath := "/home/docker/certs"
	_, err = host.RunSSHCommand(fmt.Sprintf("openssl genrsa -out %s/registry-key.pem 2048", certsBasePath))
	if err != nil {
		return err
	}
	registryCAConfPath := "/home/docker/certs/registry_ca.conf"
	_, err = host.RunSSHCommand(fmt.Sprintf("echo -e '%s' >> %s", registryCAConf, registryCAConfPath))
	if err != nil {
		return err
	}
	_, err = host.RunSSHCommand(fmt.Sprintf("openssl req -new -key %s/registry-key.pem -out %s/registry.csr -subj '/CN=%s.localdomain' -config %s", certsBasePath, certsBasePath, d.Name, registryCAConfPath))
	if err != nil {
		return err
	}
	_, err = host.RunSSHCommand(fmt.Sprintf("openssl x509 -req -in %s/registry.csr -CA '%s/ca.pem' -CAkey '%s/ca-key.pem' -CAcreateserial -out '%s/registry.pem' -days 365 -extensions v3_req -extfile %s", certsBasePath, certsBasePath, certsBasePath, certsBasePath, registryCAConfPath))
	if err != nil {
		return err
	}
	_, err = host.RunSSHCommand(fmt.Sprintf("sudo mkdir -p /var/lib/boot2docker/certs/"))
	if err != nil {
		return err
	}
	_, err = host.RunSSHCommand(fmt.Sprintf("sudo ln -s %s /etc/docker/certs.d", certsBasePath))
	if err != nil {
		return err
	}
	_, err = host.RunSSHCommand("sudo /etc/init.d/docker restart")
	time.Sleep(10 * time.Second)
	return err
}

func configureDriver(driver drivers.Driver, driverOpts map[string]interface{}) error {
	opts := &rpcdriver.RPCFlags{Values: driverOpts}
	for _, c := range driver.GetCreateFlags() {
		_, ok := opts.Values[c.String()]
		if ok == false {
			opts.Values[c.String()] = c.Default()
			if c.Default() == nil {
				opts.Values[c.String()] = false
			}
		}
	}
	if err := driver.SetConfigFromFlags(opts); err != nil {
		return fmt.Errorf("Error setting driver configurations: %s", err)
	}
	return nil
}

func (d *DockerMachine) configureHost(host *host.Host) {
	if d.tlsSupport == false {
		host.HostOptions.EngineOptions.Env = []string{"DOCKER_TLS=no"}
		host.HostOptions.EngineOptions.ArbitraryFlags = []string{fmt.Sprintf("host=tcp://0.0.0.0:%d", dockerHTTPPort)}
	}
}

func (d *DockerMachine) DeleteMachine(m *Machine) error {
	client := libmachine.NewClient(d.storePath, d.certsPath)
	defer client.Close()
	h, err := client.Load("tsuru")
	if err != nil {
		return err
	}
	err = h.Driver.Remove()
	if err != nil {
		return err
	}
	return client.Remove("tsuru")
}

func RunDriver(driverName string) error {
	switch driverName {
	case "amazonec2":
		plugin.RegisterDriver(amazonec2.NewDriver("", ""))
	case "azure":
		plugin.RegisterDriver(azure.NewDriver("", ""))
	case "digitalocean":
		plugin.RegisterDriver(digitalocean.NewDriver("", ""))
	case "exoscale":
		plugin.RegisterDriver(exoscale.NewDriver("", ""))
	case "generic":
		plugin.RegisterDriver(generic.NewDriver("", ""))
	case "google":
		plugin.RegisterDriver(google.NewDriver("", ""))
	case "hyperv":
		plugin.RegisterDriver(hyperv.NewDriver("", ""))
	case "none":
		plugin.RegisterDriver(none.NewDriver("", ""))
	case "openstack":
		plugin.RegisterDriver(openstack.NewDriver("", ""))
	case "rackspace":
		plugin.RegisterDriver(rackspace.NewDriver("", ""))
	case "softlayer":
		plugin.RegisterDriver(softlayer.NewDriver("", ""))
	case "virtualbox":
		plugin.RegisterDriver(virtualbox.NewDriver("", ""))
	case "vmwarefusion":
		plugin.RegisterDriver(vmwarefusion.NewDriver("", ""))
	case "vmwarevcloudair":
		plugin.RegisterDriver(vmwarevcloudair.NewDriver("", ""))
	case "vmwarevsphere":
		plugin.RegisterDriver(vmwarevsphere.NewDriver("", ""))
	default:
		return fmt.Errorf("Unsupported driver: %s\n", driverName)
	}
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package trigger

import (
	"fmt"

	"github.com/sirupsen/logrus"
	"k8s.io/test-infra/prow/kube"

	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/test-infra/prow/config"
	"k8s.io/test-infra/prow/github"
	"k8s.io/test-infra/prow/labels"
	"k8s.io/test-infra/prow/pjutil"
	"k8s.io/test-infra/prow/plugins"
)

func handleGenericComment(c Client, trigger plugins.Trigger, gc github.GenericCommentEvent) error {
	org := gc.Repo.Owner.Login
	repo := gc.Repo.Name
	number := gc.Number
	commentAuthor := gc.User.Login
	// Only take action when a comment is first created,
	// when it belongs to a PR,
	// and the PR is open.
	if gc.Action != github.GenericCommentActionCreated || !gc.IsPR || gc.IssueState != "open" {
		return nil
	}

	// Skip bot comments.
	botUserChecker, err := c.GitHubClient.BotUserChecker()
	if err != nil {
		return err
	}

	if botUserChecker(commentAuthor) {
		c.Logger.Debug("Comment is made by the bot, skipping.")
		return nil
	}

	refGetter := config.NewRefGetterForGitHubPullRequest(c.GitHubClient, org, repo, number)
	presubmits := getPresubmits(c.Logger, c.GitClient, c.Config, org+"/"+repo, refGetter.BaseSHA, refGetter.HeadSHA)

	// Skip comments not germane to this plugin
	if !pjutil.RetestRe.MatchString(gc.Body) &&
		!pjutil.RetestRequiredRe.MatchString(gc.Body) &&
		!pjutil.OkToTestRe.MatchString(gc.Body) &&
		!pjutil.TestAllRe.MatchString(gc.Body) &&
		!pjutil.MayNeedHelpComment(gc.Body) {
		matched := false
		for _, presubmit := range presubmits {
			matched = matched || presubmit.TriggerMatches(gc.Body)
			if matched {
				break
			}
		}
		if !matched {
			c.Logger.Debug("Comment doesn't match any triggering regex, skipping.")
			return nil
		}
	}

	// Skip untrusted users comments.
	trustedResponse, err := TrustedUser(c.GitHubClient, trigger.OnlyOrgMembers, trigger.TrustedOrg, commentAuthor, org, repo)
	if err != nil {
		return fmt.Errorf("error checking trust of %s: %w", commentAuthor, err)
	}

	trusted := trustedResponse.IsTrusted
	var l []github.Label
	if !trusted {
		// Skip untrusted PRs.
		l, trusted, err = TrustedPullRequest(c.GitHubClient, trigger, gc.IssueAuthor.Login, org, repo, number, nil)
		if err != nil {
			return err
		}
		if !trusted {
			resp := "Cannot trigger testing until a trusted user reviews the PR and leaves an `/ok-to-test` message."
			c.Logger.Infof("Commenting \"%s\".", resp)
			return c.GitHubClient.CreateComment(org, repo, number, plugins.FormatResponseRaw(gc.Body, gc.HTMLURL, gc.User.Login, resp))
		}
	}

	// At this point we can trust the PR, so we eventually update labels.
	// Ensure we have labels before test, because TrustedPullRequest() won't be called
	// when commentAuthor is trusted.
	if l == nil {
		l, err = c.GitHubClient.GetIssueLabels(org, repo, number)
		if err != nil {
			return err
		}
	}
	isOkToTest := HonorOkToTest(trigger) && pjutil.OkToTestRe.MatchString(gc.Body)
	if isOkToTest && !github.HasLabel(labels.OkToTest, l) {
		if err := c.GitHubClient.AddLabel(org, repo, number, labels.OkToTest); err != nil {
			return err
		}
	}
	if (isOkToTest || github.HasLabel(labels.OkToTest, l)) && github.HasLabel(labels.NeedsOkToTest, l) {
		if err := c.GitHubClient.RemoveLabel(org, repo, number, labels.NeedsOkToTest); err != nil {
			return err
		}
	}

	pr, err := refGetter.PullRequest()
	if err != nil {
		return err
	}
	baseSHA, err := refGetter.BaseSHA()
	if err != nil {
		return err
	}

	toTest, err := FilterPresubmits(HonorOkToTest(trigger), c.GitHubClient, gc.Body, pr, presubmits, c.Logger)
	if err != nil {
		return err
	}
	if needsHelp, note := pjutil.ShouldRespondWithHelp(gc.Body, len(toTest)); needsHelp {
		return addHelpComment(c.GitHubClient, gc.Body, org, repo, pr.Base.Ref, pr.Number, presubmits, gc.HTMLURL, commentAuthor, note, c.Logger)
	}
	// we want to be able to track re-tests separately from the general body of tests
	additionalLabels := map[string]string{}
	if pjutil.RetestRe.MatchString(gc.Body) || pjutil.RetestRequiredRe.MatchString(gc.Body) {
		additionalLabels[kube.RetestLabel] = "true"
	}
	// run failed github actions
	if pjutil.RetestRe.MatchString(gc.Body) || pjutil.TestAllRe.MatchString(gc.Body) {
		headSHA, err := refGetter.HeadSHA()
		if err != nil {
			c.Logger.Warnf("headSHA unvailable, failed github actions for pr will not be triggered: %v", pr)
		} else {
			// pr.
			failedRuns, err := c.GitHubClient.GetFailedActionRunsByHeadBranch(org, repo, pr.Head.Ref, headSHA)
			if err != nil {
				c.Logger.Errorf("unable to get failed github action runs for branch %v", pr.Head.Ref)
			} else {
				for _, run := range failedRuns {
					go func() {
						if err := c.GitHubClient.TriggerGithubWorkflow(org, repo, run.ID); err != nil {
							c.Logger.Errorf("attempt to trigger github run failed \"%s\"(%d) for %s/%s: %w", run.Name, run.ID, org, repo, err)
						}
					}()
				}
			}
		}
	}
	return RunRequestedWithLabels(c, pr, baseSHA, toTest, gc.GUID, additionalLabels)
}

func HonorOkToTest(trigger plugins.Trigger) bool {
	return !trigger.IgnoreOkToTest
}

type GitHubClient interface {
	GetCombinedStatus(org, repo, ref string) (*github.CombinedStatus, error)
	GetPullRequestChanges(org, repo string, number int) ([]github.PullRequestChange, error)
}

// FilterPresubmits determines which presubmits should run. We only want to
// trigger jobs that should run, but the pool of jobs we filter to those that
// should run depends on the type of trigger we just got:
//  - if we get a /test foo, we only want to consider those jobs that match;
//    jobs will default to run unless we can determine they shouldn't
//  - if we got a /retest, we only want to consider those jobs that have
//    already run and posted failing contexts to the PR or those jobs that
//    have not yet run but would otherwise match /test all; jobs will default
//    to run unless we can determine they shouldn't
//  - if we got a /test all or an /ok-to-test, we want to consider any job
//    that doesn't explicitly require a human trigger comment; jobs will
//    default to not run unless we can determine that they should
// If a comment that we get matches more than one of the above patterns, we
// consider the set of matching presubmits the union of the results from the
// matching cases.
func FilterPresubmits(honorOkToTest bool, gitHubClient GitHubClient, body string, pr *github.PullRequest, presubmits []config.Presubmit, logger *logrus.Entry) ([]config.Presubmit, error) {
	org, repo, sha := pr.Base.Repo.Owner.Login, pr.Base.Repo.Name, pr.Head.SHA

	contextGetter := func() (sets.String, sets.String, error) {
		combinedStatus, err := gitHubClient.GetCombinedStatus(org, repo, sha)
		if err != nil {
			return nil, nil, err
		}
		failedContexts, allContexts := getContexts(combinedStatus)
		return failedContexts, allContexts, nil
	}

	filter, err := pjutil.PresubmitFilter(honorOkToTest, contextGetter, body, logger)
	if err != nil {
		return nil, err
	}

	number, branch := pr.Number, pr.Base.Ref
	changes := config.NewGitHubDeferredChangedFilesProvider(gitHubClient, org, repo, number)
	return pjutil.FilterPresubmits(filter, changes, branch, presubmits, logger)
}

func getContexts(combinedStatus *github.CombinedStatus) (sets.String, sets.String) {
	allContexts := sets.String{}
	failedContexts := sets.String{}
	if combinedStatus != nil {
		for _, status := range combinedStatus.Statuses {
			allContexts.Insert(status.Context)
			if status.State == github.StatusError || status.State == github.StatusFailure {
				failedContexts.Insert(status.Context)
			}
		}
	}
	return failedContexts, allContexts
}

func addHelpComment(githubClient githubClient, body, org, repo, branch string, number int, presubmits []config.Presubmit, HTMLURL, user, note string, logger *logrus.Entry) error {
	changes := config.NewGitHubDeferredChangedFilesProvider(githubClient, org, repo, number)
	testAllNames, optionalJobsCommands, requiredJobsCommands, err := pjutil.AvailablePresubmits(changes, org, repo, branch, presubmits, logger)
	if err != nil {
		return err
	}

	resp := pjutil.HelpMessage(org, repo, branch, note, testAllNames, optionalJobsCommands, requiredJobsCommands)
	return githubClient.CreateComment(org, repo, number, plugins.FormatResponseRaw(body, HTMLURL, user, resp))
}
<commit_msg>fix go problems<commit_after>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package trigger

import (
	"fmt"

	"github.com/sirupsen/logrus"
	"k8s.io/test-infra/prow/kube"

	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/test-infra/prow/config"
	"k8s.io/test-infra/prow/github"
	"k8s.io/test-infra/prow/labels"
	"k8s.io/test-infra/prow/pjutil"
	"k8s.io/test-infra/prow/plugins"
)

func handleGenericComment(c Client, trigger plugins.Trigger, gc github.GenericCommentEvent) error {
	org := gc.Repo.Owner.Login
	repo := gc.Repo.Name
	number := gc.Number
	commentAuthor := gc.User.Login
	// Only take action when a comment is first created,
	// when it belongs to a PR,
	// and the PR is open.
	if gc.Action != github.GenericCommentActionCreated || !gc.IsPR || gc.IssueState != "open" {
		return nil
	}

	// Skip bot comments.
	botUserChecker, err := c.GitHubClient.BotUserChecker()
	if err != nil {
		return err
	}

	if botUserChecker(commentAuthor) {
		c.Logger.Debug("Comment is made by the bot, skipping.")
		return nil
	}

	refGetter := config.NewRefGetterForGitHubPullRequest(c.GitHubClient, org, repo, number)
	presubmits := getPresubmits(c.Logger, c.GitClient, c.Config, org+"/"+repo, refGetter.BaseSHA, refGetter.HeadSHA)

	// Skip comments not germane to this plugin
	if !pjutil.RetestRe.MatchString(gc.Body) &&
		!pjutil.RetestRequiredRe.MatchString(gc.Body) &&
		!pjutil.OkToTestRe.MatchString(gc.Body) &&
		!pjutil.TestAllRe.MatchString(gc.Body) &&
		!pjutil.MayNeedHelpComment(gc.Body) {
		matched := false
		for _, presubmit := range presubmits {
			matched = matched || presubmit.TriggerMatches(gc.Body)
			if matched {
				break
			}
		}
		if !matched {
			c.Logger.Debug("Comment doesn't match any triggering regex, skipping.")
			return nil
		}
	}

	// Skip untrusted users comments.
	trustedResponse, err := TrustedUser(c.GitHubClient, trigger.OnlyOrgMembers, trigger.TrustedOrg, commentAuthor, org, repo)
	if err != nil {
		return fmt.Errorf("error checking trust of %s: %w", commentAuthor, err)
	}

	trusted := trustedResponse.IsTrusted
	var l []github.Label
	if !trusted {
		// Skip untrusted PRs.
		l, trusted, err = TrustedPullRequest(c.GitHubClient, trigger, gc.IssueAuthor.Login, org, repo, number, nil)
		if err != nil {
			return err
		}
		if !trusted {
			resp := "Cannot trigger testing until a trusted user reviews the PR and leaves an `/ok-to-test` message."
			c.Logger.Infof("Commenting \"%s\".", resp)
			return c.GitHubClient.CreateComment(org, repo, number, plugins.FormatResponseRaw(gc.Body, gc.HTMLURL, gc.User.Login, resp))
		}
	}

	// At this point we can trust the PR, so we eventually update labels.
	// Ensure we have labels before test, because TrustedPullRequest() won't be called
	// when commentAuthor is trusted.
	if l == nil {
		l, err = c.GitHubClient.GetIssueLabels(org, repo, number)
		if err != nil {
			return err
		}
	}
	isOkToTest := HonorOkToTest(trigger) && pjutil.OkToTestRe.MatchString(gc.Body)
	if isOkToTest && !github.HasLabel(labels.OkToTest, l) {
		if err := c.GitHubClient.AddLabel(org, repo, number, labels.OkToTest); err != nil {
			return err
		}
	}
	if (isOkToTest || github.HasLabel(labels.OkToTest, l)) && github.HasLabel(labels.NeedsOkToTest, l) {
		if err := c.GitHubClient.RemoveLabel(org, repo, number, labels.NeedsOkToTest); err != nil {
			return err
		}
	}

	pr, err := refGetter.PullRequest()
	if err != nil {
		return err
	}
	baseSHA, err := refGetter.BaseSHA()
	if err != nil {
		return err
	}

	toTest, err := FilterPresubmits(HonorOkToTest(trigger), c.GitHubClient, gc.Body, pr, presubmits, c.Logger)
	if err != nil {
		return err
	}
	if needsHelp, note := pjutil.ShouldRespondWithHelp(gc.Body, len(toTest)); needsHelp {
		return addHelpComment(c.GitHubClient, gc.Body, org, repo, pr.Base.Ref, pr.Number, presubmits, gc.HTMLURL, commentAuthor, note, c.Logger)
	}
	// we want to be able to track re-tests separately from the general body of tests
	additionalLabels := map[string]string{}
	if pjutil.RetestRe.MatchString(gc.Body) || pjutil.RetestRequiredRe.MatchString(gc.Body) {
		additionalLabels[kube.RetestLabel] = "true"
	}
	// run failed github actions
	if pjutil.RetestRe.MatchString(gc.Body) || pjutil.TestAllRe.MatchString(gc.Body) {
		headSHA, err := refGetter.HeadSHA()
		if err != nil {
			c.Logger.Warnf("headSHA unvailable, failed github actions for pr will not be triggered: %v", pr)
		} else {
			// pr.
			failedRuns, err := c.GitHubClient.GetFailedActionRunsByHeadBranch(org, repo, pr.Head.Ref, headSHA)
			if err != nil {
				c.Logger.Errorf("unable to get failed github action runs for branch %v", pr.Head.Ref)
			} else {
				for _, run := range failedRuns {
					runID, runName := run.ID, run.Name
					go func() {
						if err := c.GitHubClient.TriggerGithubWorkflow(org, repo, runID); err != nil {
							c.Logger.Errorf("attempt to trigger github run failed \"%s\"(%d) for %s/%s: %v", runName, runID, org, repo, err)
						}
					}()
				}
			}
		}
	}
	return RunRequestedWithLabels(c, pr, baseSHA, toTest, gc.GUID, additionalLabels)
}

func HonorOkToTest(trigger plugins.Trigger) bool {
	return !trigger.IgnoreOkToTest
}

type GitHubClient interface {
	GetCombinedStatus(org, repo, ref string) (*github.CombinedStatus, error)
	GetPullRequestChanges(org, repo string, number int) ([]github.PullRequestChange, error)
}

// FilterPresubmits determines which presubmits should run. We only want to
// trigger jobs that should run, but the pool of jobs we filter to those that
// should run depends on the type of trigger we just got:
//  - if we get a /test foo, we only want to consider those jobs that match;
//    jobs will default to run unless we can determine they shouldn't
//  - if we got a /retest, we only want to consider those jobs that have
//    already run and posted failing contexts to the PR or those jobs that
//    have not yet run but would otherwise match /test all; jobs will default
//    to run unless we can determine they shouldn't
//  - if we got a /test all or an /ok-to-test, we want to consider any job
//    that doesn't explicitly require a human trigger comment; jobs will
//    default to not run unless we can determine that they should
// If a comment that we get matches more than one of the above patterns, we
// consider the set of matching presubmits the union of the results from the
// matching cases.
func FilterPresubmits(honorOkToTest bool, gitHubClient GitHubClient, body string, pr *github.PullRequest, presubmits []config.Presubmit, logger *logrus.Entry) ([]config.Presubmit, error) {
	org, repo, sha := pr.Base.Repo.Owner.Login, pr.Base.Repo.Name, pr.Head.SHA

	contextGetter := func() (sets.String, sets.String, error) {
		combinedStatus, err := gitHubClient.GetCombinedStatus(org, repo, sha)
		if err != nil {
			return nil, nil, err
		}
		failedContexts, allContexts := getContexts(combinedStatus)
		return failedContexts, allContexts, nil
	}

	filter, err := pjutil.PresubmitFilter(honorOkToTest, contextGetter, body, logger)
	if err != nil {
		return nil, err
	}

	number, branch := pr.Number, pr.Base.Ref
	changes := config.NewGitHubDeferredChangedFilesProvider(gitHubClient, org, repo, number)
	return pjutil.FilterPresubmits(filter, changes, branch, presubmits, logger)
}

func getContexts(combinedStatus *github.CombinedStatus) (sets.String, sets.String) {
	allContexts := sets.String{}
	failedContexts := sets.String{}
	if combinedStatus != nil {
		for _, status := range combinedStatus.Statuses {
			allContexts.Insert(status.Context)
			if status.State == github.StatusError || status.State == github.StatusFailure {
				failedContexts.Insert(status.Context)
			}
		}
	}
	return failedContexts, allContexts
}

func addHelpComment(githubClient githubClient, body, org, repo, branch string, number int, presubmits []config.Presubmit, HTMLURL, user, note string, logger *logrus.Entry) error {
	changes := config.NewGitHubDeferredChangedFilesProvider(githubClient, org, repo, number)
	testAllNames, optionalJobsCommands, requiredJobsCommands, err := pjutil.AvailablePresubmits(changes, org, repo, branch, presubmits, logger)
	if err != nil {
		return err
	}

	resp := pjutil.HelpMessage(org, repo, branch, note, testAllNames, optionalJobsCommands, requiredJobsCommands)
	return githubClient.CreateComment(org, repo, number, plugins.FormatResponseRaw(body, HTMLURL, user, resp))
}
<|endoftext|>