<commit_before>/*
Copyright 2019 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 apply

import (
	"fmt"
	"io"
	"strings"

	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/cli-runtime/pkg/printers"
	"k8s.io/client-go/dynamic"
	cmdutil "k8s.io/kubectl/pkg/cmd/util"
)

type pruner struct {
	mapper        meta.RESTMapper
	dynamicClient dynamic.Interface

	visitedUids       sets.String
	visitedNamespaces sets.String
	labelSelector     string
	fieldSelector     string

	cascade        bool
	dryRunStrategy cmdutil.DryRunStrategy
	gracePeriod    int

	toPrinter func(string) (printers.ResourcePrinter, error)

	out io.Writer
}

func newPruner(o *ApplyOptions) pruner {
	return pruner{
		mapper:        o.Mapper,
		dynamicClient: o.DynamicClient,

		labelSelector:     o.Selector,
		visitedUids:       o.VisitedUids,
		visitedNamespaces: o.VisitedNamespaces,

		cascade:        o.DeleteOptions.Cascade,
		dryRunStrategy: o.DryRunStrategy,
		gracePeriod:    o.DeleteOptions.GracePeriod,

		toPrinter: o.ToPrinter,

		out: o.Out,
	}
}

func (p *pruner) pruneAll(o *ApplyOptions) error {

	namespacedRESTMappings, nonNamespacedRESTMappings, err := getRESTMappings(o.Mapper, &(o.PruneResources))
	if err != nil {
		return fmt.Errorf("error retrieving RESTMappings to prune: %v", err)
	}

	for n := range p.visitedNamespaces {
		if len(o.Namespace) != 0 && n != o.Namespace {
			continue
		}
		for _, m := range namespacedRESTMappings {
			if err := p.prune(n, m); err != nil {
				return fmt.Errorf("error pruning namespaced object %v: %v", m.GroupVersionKind, err)
			}
		}
	}
	for _, m := range nonNamespacedRESTMappings {
		if err := p.prune(metav1.NamespaceNone, m); err != nil {
			return fmt.Errorf("error pruning nonNamespaced object %v: %v", m.GroupVersionKind, err)
		}
	}

	return nil
}

func (p *pruner) prune(namespace string, mapping *meta.RESTMapping) error {
	objList, err := p.dynamicClient.Resource(mapping.Resource).
		Namespace(namespace).
		List(metav1.ListOptions{
			LabelSelector: p.labelSelector,
			FieldSelector: p.fieldSelector,
		})
	if err != nil {
		return err
	}

	objs, err := meta.ExtractList(objList)
	if err != nil {
		return err
	}

	for _, obj := range objs {
		metadata, err := meta.Accessor(obj)
		if err != nil {
			return err
		}
		annots := metadata.GetAnnotations()
		if _, ok := annots[corev1.LastAppliedConfigAnnotation]; !ok {
			// don't prune resources not created with apply
			continue
		}
		uid := metadata.GetUID()
		if p.visitedUids.Has(string(uid)) {
			continue
		}
		name := metadata.GetName()
		if p.dryRunStrategy != cmdutil.DryRunClient {
			if err := p.delete(namespace, name, mapping); err != nil {
				return err
			}
		}

		printer, err := p.toPrinter("pruned")
		if err != nil {
			return err
		}
		printer.PrintObj(obj, p.out)
	}
	return nil
}

func (p *pruner) delete(namespace, name string, mapping *meta.RESTMapping) error {
	return runDelete(namespace, name, mapping, p.dynamicClient, p.cascade, p.gracePeriod, p.dryRunStrategy == cmdutil.DryRunServer)
}

func runDelete(namespace, name string, mapping *meta.RESTMapping, c dynamic.Interface, cascade bool, gracePeriod int, serverDryRun bool) error {
	options := &metav1.DeleteOptions{}
	if gracePeriod >= 0 {
		options = metav1.NewDeleteOptions(int64(gracePeriod))
	}
	if serverDryRun {
		options.DryRun = []string{metav1.DryRunAll}
	}
	policy := metav1.DeletePropagationForeground
	if !cascade {
		policy = metav1.DeletePropagationOrphan
	}
	options.PropagationPolicy = &policy
	return c.Resource(mapping.Resource).Namespace(namespace).Delete(name, options)
}

type pruneResource struct {
	group      string
	version    string
	kind       string
	namespaced bool
}

func (pr pruneResource) String() string {
	return fmt.Sprintf("%v/%v, Kind=%v, Namespaced=%v", pr.group, pr.version, pr.kind, pr.namespaced)
}

func getRESTMappings(mapper meta.RESTMapper, pruneResources *[]pruneResource) (namespaced, nonNamespaced []*meta.RESTMapping, err error) {
	if len(*pruneResources) == 0 {
		// default whitelist
		// TODO: need to handle the older api versions - e.g. v1beta1 jobs. Github issue: #35991
		*pruneResources = []pruneResource{
			{"", "v1", "ConfigMap", true},
			{"", "v1", "Endpoints", true},
			{"", "v1", "Namespace", false},
			{"", "v1", "PersistentVolumeClaim", true},
			{"", "v1", "PersistentVolume", false},
			{"", "v1", "Pod", true},
			{"", "v1", "ReplicationController", true},
			{"", "v1", "Secret", true},
			{"", "v1", "Service", true},
			{"batch", "v1", "Job", true},
			{"batch", "v1beta1", "CronJob", true},
			{"extensions", "v1beta1", "Ingress", true},
			{"apps", "v1", "DaemonSet", true},
			{"apps", "v1", "Deployment", true},
			{"apps", "v1", "ReplicaSet", true},
			{"apps", "v1", "StatefulSet", true},
		}
	}

	for _, resource := range *pruneResources {
		addedMapping, err := mapper.RESTMapping(schema.GroupKind{Group: resource.group, Kind: resource.kind}, resource.version)
		if err != nil {
			return nil, nil, fmt.Errorf("invalid resource %v: %v", resource, err)
		}
		if resource.namespaced {
			namespaced = append(namespaced, addedMapping)
		} else {
			nonNamespaced = append(nonNamespaced, addedMapping)
		}
	}

	return namespaced, nonNamespaced, nil
}

func parsePruneResources(mapper meta.RESTMapper, gvks []string) ([]pruneResource, error) {
	pruneResources := []pruneResource{}
	for _, groupVersionKind := range gvks {
		gvk := strings.Split(groupVersionKind, "/")
		if len(gvk) != 3 {
			return nil, fmt.Errorf("invalid GroupVersionKind format: %v, please follow <group/version/kind>", groupVersionKind)
		}

		if gvk[0] == "core" {
			gvk[0] = ""
		}
		mapping, err := mapper.RESTMapping(schema.GroupKind{Group: gvk[0], Kind: gvk[2]}, gvk[1])
		if err != nil {
			return pruneResources, err
		}
		var namespaced bool
		namespaceScope := mapping.Scope.Name()
		switch namespaceScope {
		case meta.RESTScopeNameNamespace:
			namespaced = true
		case meta.RESTScopeNameRoot:
			namespaced = false
		default:
			return pruneResources, fmt.Errorf("Unknown namespace scope: %q", namespaceScope)
		}

		pruneResources = append(pruneResources, pruneResource{gvk[0], gvk[1], gvk[2], namespaced})
	}
	return pruneResources, nil
}
<commit_msg>client-go dynamic client: update DeleteOptions callers<commit_after>/*
Copyright 2019 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 apply

import (
	"fmt"
	"io"
	"strings"

	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/cli-runtime/pkg/printers"
	"k8s.io/client-go/dynamic"
	cmdutil "k8s.io/kubectl/pkg/cmd/util"
)

type pruner struct {
	mapper        meta.RESTMapper
	dynamicClient dynamic.Interface

	visitedUids       sets.String
	visitedNamespaces sets.String
	labelSelector     string
	fieldSelector     string

	cascade        bool
	dryRunStrategy cmdutil.DryRunStrategy
	gracePeriod    int

	toPrinter func(string) (printers.ResourcePrinter, error)

	out io.Writer
}

func newPruner(o *ApplyOptions) pruner {
	return pruner{
		mapper:        o.Mapper,
		dynamicClient: o.DynamicClient,

		labelSelector:     o.Selector,
		visitedUids:       o.VisitedUids,
		visitedNamespaces: o.VisitedNamespaces,

		cascade:        o.DeleteOptions.Cascade,
		dryRunStrategy: o.DryRunStrategy,
		gracePeriod:    o.DeleteOptions.GracePeriod,

		toPrinter: o.ToPrinter,

		out: o.Out,
	}
}

func (p *pruner) pruneAll(o *ApplyOptions) error {

	namespacedRESTMappings, nonNamespacedRESTMappings, err := getRESTMappings(o.Mapper, &(o.PruneResources))
	if err != nil {
		return fmt.Errorf("error retrieving RESTMappings to prune: %v", err)
	}

	for n := range p.visitedNamespaces {
		if len(o.Namespace) != 0 && n != o.Namespace {
			continue
		}
		for _, m := range namespacedRESTMappings {
			if err := p.prune(n, m); err != nil {
				return fmt.Errorf("error pruning namespaced object %v: %v", m.GroupVersionKind, err)
			}
		}
	}
	for _, m := range nonNamespacedRESTMappings {
		if err := p.prune(metav1.NamespaceNone, m); err != nil {
			return fmt.Errorf("error pruning nonNamespaced object %v: %v", m.GroupVersionKind, err)
		}
	}

	return nil
}

func (p *pruner) prune(namespace string, mapping *meta.RESTMapping) error {
	objList, err := p.dynamicClient.Resource(mapping.Resource).
		Namespace(namespace).
		List(metav1.ListOptions{
			LabelSelector: p.labelSelector,
			FieldSelector: p.fieldSelector,
		})
	if err != nil {
		return err
	}

	objs, err := meta.ExtractList(objList)
	if err != nil {
		return err
	}

	for _, obj := range objs {
		metadata, err := meta.Accessor(obj)
		if err != nil {
			return err
		}
		annots := metadata.GetAnnotations()
		if _, ok := annots[corev1.LastAppliedConfigAnnotation]; !ok {
			// don't prune resources not created with apply
			continue
		}
		uid := metadata.GetUID()
		if p.visitedUids.Has(string(uid)) {
			continue
		}
		name := metadata.GetName()
		if p.dryRunStrategy != cmdutil.DryRunClient {
			if err := p.delete(namespace, name, mapping); err != nil {
				return err
			}
		}

		printer, err := p.toPrinter("pruned")
		if err != nil {
			return err
		}
		printer.PrintObj(obj, p.out)
	}
	return nil
}

func (p *pruner) delete(namespace, name string, mapping *meta.RESTMapping) error {
	return runDelete(namespace, name, mapping, p.dynamicClient, p.cascade, p.gracePeriod, p.dryRunStrategy == cmdutil.DryRunServer)
}

func runDelete(namespace, name string, mapping *meta.RESTMapping, c dynamic.Interface, cascade bool, gracePeriod int, serverDryRun bool) error {
	options := metav1.DeleteOptions{}
	if gracePeriod >= 0 {
		options = *metav1.NewDeleteOptions(int64(gracePeriod))
	}
	if serverDryRun {
		options.DryRun = []string{metav1.DryRunAll}
	}
	policy := metav1.DeletePropagationForeground
	if !cascade {
		policy = metav1.DeletePropagationOrphan
	}
	options.PropagationPolicy = &policy
	return c.Resource(mapping.Resource).Namespace(namespace).Delete(name, options)
}

type pruneResource struct {
	group      string
	version    string
	kind       string
	namespaced bool
}

func (pr pruneResource) String() string {
	return fmt.Sprintf("%v/%v, Kind=%v, Namespaced=%v", pr.group, pr.version, pr.kind, pr.namespaced)
}

func getRESTMappings(mapper meta.RESTMapper, pruneResources *[]pruneResource) (namespaced, nonNamespaced []*meta.RESTMapping, err error) {
	if len(*pruneResources) == 0 {
		// default whitelist
		// TODO: need to handle the older api versions - e.g. v1beta1 jobs. Github issue: #35991
		*pruneResources = []pruneResource{
			{"", "v1", "ConfigMap", true},
			{"", "v1", "Endpoints", true},
			{"", "v1", "Namespace", false},
			{"", "v1", "PersistentVolumeClaim", true},
			{"", "v1", "PersistentVolume", false},
			{"", "v1", "Pod", true},
			{"", "v1", "ReplicationController", true},
			{"", "v1", "Secret", true},
			{"", "v1", "Service", true},
			{"batch", "v1", "Job", true},
			{"batch", "v1beta1", "CronJob", true},
			{"extensions", "v1beta1", "Ingress", true},
			{"apps", "v1", "DaemonSet", true},
			{"apps", "v1", "Deployment", true},
			{"apps", "v1", "ReplicaSet", true},
			{"apps", "v1", "StatefulSet", true},
		}
	}

	for _, resource := range *pruneResources {
		addedMapping, err := mapper.RESTMapping(schema.GroupKind{Group: resource.group, Kind: resource.kind}, resource.version)
		if err != nil {
			return nil, nil, fmt.Errorf("invalid resource %v: %v", resource, err)
		}
		if resource.namespaced {
			namespaced = append(namespaced, addedMapping)
		} else {
			nonNamespaced = append(nonNamespaced, addedMapping)
		}
	}

	return namespaced, nonNamespaced, nil
}

func parsePruneResources(mapper meta.RESTMapper, gvks []string) ([]pruneResource, error) {
	pruneResources := []pruneResource{}
	for _, groupVersionKind := range gvks {
		gvk := strings.Split(groupVersionKind, "/")
		if len(gvk) != 3 {
			return nil, fmt.Errorf("invalid GroupVersionKind format: %v, please follow <group/version/kind>", groupVersionKind)
		}

		if gvk[0] == "core" {
			gvk[0] = ""
		}
		mapping, err := mapper.RESTMapping(schema.GroupKind{Group: gvk[0], Kind: gvk[2]}, gvk[1])
		if err != nil {
			return pruneResources, err
		}
		var namespaced bool
		namespaceScope := mapping.Scope.Name()
		switch namespaceScope {
		case meta.RESTScopeNameNamespace:
			namespaced = true
		case meta.RESTScopeNameRoot:
			namespaced = false
		default:
			return pruneResources, fmt.Errorf("Unknown namespace scope: %q", namespaceScope)
		}

		pruneResources = append(pruneResources, pruneResource{gvk[0], gvk[1], gvk[2], namespaced})
	}
	return pruneResources, nil
}
<|endoftext|>


<commit_before>/*
   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 compose

import (
	"context"
	"encoding/json"
	"fmt"
	"strings"

	"github.com/docker/compose/v2/pkg/api"

	"github.com/compose-spec/compose-go/types"
	"github.com/docker/cli/cli/config/configfile"
	moby "github.com/docker/docker/api/types"
	"github.com/docker/docker/client"
	"github.com/sanathkr/go-yaml"
)

// NewComposeService create a local implementation of the compose.Service API
func NewComposeService(apiClient client.APIClient, configFile *configfile.ConfigFile) api.Service {
	return &composeService{
		apiClient:  apiClient,
		configFile: configFile,
	}
}

type composeService struct {
	apiClient  client.APIClient
	configFile *configfile.ConfigFile
}

func getCanonicalContainerName(c moby.Container) string {
	// Names return container canonical name /foo  + link aliases /linked_by/foo
	for _, name := range c.Names {
		if strings.LastIndex(name, "/") == 0 {
			return name[1:]
		}
	}
	return c.Names[0][1:]
}

func getContainerNameWithoutProject(c moby.Container) string {
	name := getCanonicalContainerName(c)
	project := c.Labels[api.ProjectLabel]
	prefix := fmt.Sprintf("%s_%s_", project, c.Labels[api.ServiceLabel])
	if strings.HasPrefix(name, prefix) {
		return name[len(project)+1:]
	}
	return name
}

func (s *composeService) Convert(ctx context.Context, project *types.Project, options api.ConvertOptions) ([]byte, error) {
	switch options.Format {
	case "json":
		return json.MarshalIndent(project, "", "  ")
	case "yaml":
		return yaml.Marshal(project)
	default:
		return nil, fmt.Errorf("unsupported format %q", options)
	}
}
<commit_msg>`compose config` to espace `$` signs so compose file is still valid<commit_after>/*
   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 compose

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"strings"

	"github.com/docker/compose/v2/pkg/api"

	"github.com/compose-spec/compose-go/types"
	"github.com/docker/cli/cli/config/configfile"
	moby "github.com/docker/docker/api/types"
	"github.com/docker/docker/client"
	"github.com/sanathkr/go-yaml"
)

// NewComposeService create a local implementation of the compose.Service API
func NewComposeService(apiClient client.APIClient, configFile *configfile.ConfigFile) api.Service {
	return &composeService{
		apiClient:  apiClient,
		configFile: configFile,
	}
}

type composeService struct {
	apiClient  client.APIClient
	configFile *configfile.ConfigFile
}

func getCanonicalContainerName(c moby.Container) string {
	// Names return container canonical name /foo  + link aliases /linked_by/foo
	for _, name := range c.Names {
		if strings.LastIndex(name, "/") == 0 {
			return name[1:]
		}
	}
	return c.Names[0][1:]
}

func getContainerNameWithoutProject(c moby.Container) string {
	name := getCanonicalContainerName(c)
	project := c.Labels[api.ProjectLabel]
	prefix := fmt.Sprintf("%s_%s_", project, c.Labels[api.ServiceLabel])
	if strings.HasPrefix(name, prefix) {
		return name[len(project)+1:]
	}
	return name
}

func (s *composeService) Convert(ctx context.Context, project *types.Project, options api.ConvertOptions) ([]byte, error) {
	switch options.Format {
	case "json":
		marshal, err := json.MarshalIndent(project, "", "  ")
		if err != nil {
			return nil, err
		}
		return escapeDollarSign(marshal), nil
	case "yaml":
		marshal, err := yaml.Marshal(project)
		if err != nil {
			return nil, err
		}
		return escapeDollarSign(marshal), nil
	default:
		return nil, fmt.Errorf("unsupported format %q", options)
	}
}

func escapeDollarSign(marshal []byte) []byte {
	dollar := []byte{'$'}
	escDollar := []byte{'$', '$'}
	return bytes.ReplaceAll(marshal, dollar, escDollar)
}
<|endoftext|>


<commit_before>package ca

import (
	"bytes"
	"crypto/rand"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"fmt"
	"math/big"
	"time"

	"github.com/golang/glog"
	"github.com/jetstack-experimental/cert-manager/pkg/apis/certmanager/v1alpha1"
	"github.com/jetstack-experimental/cert-manager/pkg/util/kube"
	"github.com/jetstack-experimental/cert-manager/pkg/util/pki"
	"k8s.io/api/core/v1"
	k8sErrors "k8s.io/apimachinery/pkg/api/errors"
)

const (
	errorGetCertKeyPair = "ErrGetCertKeyPair"
	errorIssueCert      = "ErrIssueCert"

	successCertIssued = "CertIssueSuccess"

	messageErrorGetCertKeyPair = "Error getting keypair for certificate: "
	messageErrorIssueCert      = "Error issuing TLS certificate: "

	messageCertIssued = "Certificate issued successfully"
)

const (
	// certificateDuration of 1 year
	certificateDuration = time.Hour * 24 * 365
	defaultOrganization = "cert-manager"
)

func (c *CA) Issue(crt *v1alpha1.Certificate) (v1alpha1.CertificateStatus, []byte, []byte, error) {
	update := crt.DeepCopy()

	signeeKey, err := kube.SecretTLSKey(c.secretsLister, c.issuer.Namespace, crt.Spec.SecretName)

	if k8sErrors.IsNotFound(err) {
		signeeKey, err = pki.GenerateRSAPrivateKey(2048)
	}

	if err != nil {
		s := messageErrorGetCertKeyPair + err.Error()
		glog.Info(s)
		c.recorder.Event(update, v1.EventTypeWarning, errorGetCertKeyPair, s)
		update.UpdateStatusCondition(v1alpha1.CertificateConditionReady, v1alpha1.ConditionFalse, errorGetCertKeyPair, s)
		return update.Status, nil, nil, err
	}

	certPem, err := c.obtainCertificate(crt, signeeKey)

	if err != nil {
		s := messageErrorIssueCert + err.Error()
		glog.Info(s)
		c.recorder.Event(update, v1.EventTypeWarning, errorIssueCert, s)
		update.UpdateStatusCondition(v1alpha1.CertificateConditionReady, v1alpha1.ConditionFalse, errorIssueCert, s)
		return update.Status, nil, nil, err
	}

	s := messageCertIssued
	glog.Info(s)
	c.recorder.Event(update, v1.EventTypeNormal, successCertIssued, s)
	update.UpdateStatusCondition(v1alpha1.CertificateConditionReady, v1alpha1.ConditionTrue, successCertIssued, s)

	return update.Status, pki.EncodePKCS1PrivateKey(signeeKey), certPem, nil
}

func (c *CA) obtainCertificate(crt *v1alpha1.Certificate, signeeKey interface{}) ([]byte, error) {
	signerCert, err := kube.SecretTLSCert(c.secretsLister, c.issuer.Namespace, c.issuer.Spec.CA.SecretRef.Name)

	if err != nil {
		return nil, fmt.Errorf("error getting issuer certificate: %s", err.Error())
	}

	signerKey, err := kube.SecretTLSKey(c.secretsLister, c.issuer.Namespace, c.issuer.Spec.CA.SecretRef.Name)

	if err != nil {
		return nil, fmt.Errorf("error getting issuer private key: %s", err.Error())
	}

	crtPem, _, err := signCertificate(crt, signerCert, signeeKey, signerKey)

	if err != nil {
		return nil, err
	}

	return crtPem, nil
}

func createCertificateTemplate(crt *v1alpha1.Certificate, publicKey interface{}) (*x509.Certificate, error) {
	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		return nil, fmt.Errorf("failed to generate serial number: %s", err.Error())
	}

	cert := &x509.Certificate{
		Version:               3,
		BasicConstraintsValid: true,
		SerialNumber:          serialNumber,
		SignatureAlgorithm:    x509.SHA256WithRSA,
		PublicKey:             publicKey,
		Subject: pkix.Name{
			Organization: []string{defaultOrganization},
			CommonName:   crt.Spec.Domains[0],
		},
		NotBefore: time.Now(),
		NotAfter:  time.Now().Add(certificateDuration),
		// see http://golang.org/pkg/crypto/x509/#KeyUsage
		KeyUsage: x509.KeyUsageDigitalSignature,
		DNSNames: crt.Spec.Domains,
	}
	return cert, nil
}

// signCertificate returns a signed x509.Certificate object for the given
// *v1alpha1.Certificate crt.
// publicKey is the public key of the signee, and signerKey is the private
// key of the signer.
func signCertificate(crt *v1alpha1.Certificate, issuerCert *x509.Certificate, publicKey interface{}, signerKey interface{}) ([]byte, *x509.Certificate, error) {
	template, err := createCertificateTemplate(crt, publicKey)
	if err != nil {
		return nil, nil, fmt.Errorf("error creating x509 certificate template: %s", err.Error())
	}

	derBytes, err := x509.CreateCertificate(rand.Reader, template, issuerCert, publicKey, signerKey)

	if err != nil {
		return nil, nil, fmt.Errorf("error creating x509 certificate: %s", err.Error())
	}

	cert, err := pki.DecodeDERCertificateBytes(derBytes)

	if err != nil {
		return nil, nil, fmt.Errorf("error decoding DER certificate bytes: %s", err.Error())
	}

	pemBytes := bytes.NewBuffer([]byte{})
	err = pem.Encode(pemBytes, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
	if err != nil {
		return nil, nil, fmt.Errorf("error encoding certificate PEM: %s", err.Error())
	}
	return pemBytes.Bytes(), cert, err
}
<commit_msg>Fix passing public key to obtainCertificate<commit_after>package ca

import (
	"bytes"
	"crypto/rand"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"fmt"
	"math/big"
	"time"

	"github.com/golang/glog"
	"github.com/jetstack-experimental/cert-manager/pkg/apis/certmanager/v1alpha1"
	"github.com/jetstack-experimental/cert-manager/pkg/util/kube"
	"github.com/jetstack-experimental/cert-manager/pkg/util/pki"
	"k8s.io/api/core/v1"
	k8sErrors "k8s.io/apimachinery/pkg/api/errors"
)

const (
	errorGetCertKeyPair = "ErrGetCertKeyPair"
	errorIssueCert      = "ErrIssueCert"

	successCertIssued = "CertIssueSuccess"

	messageErrorGetCertKeyPair = "Error getting keypair for certificate: "
	messageErrorIssueCert      = "Error issuing TLS certificate: "

	messageCertIssued = "Certificate issued successfully"
)

const (
	// certificateDuration of 1 year
	certificateDuration = time.Hour * 24 * 365
	defaultOrganization = "cert-manager"
)

func (c *CA) Issue(crt *v1alpha1.Certificate) (v1alpha1.CertificateStatus, []byte, []byte, error) {
	update := crt.DeepCopy()

	signeeKey, err := kube.SecretTLSKey(c.secretsLister, c.issuer.Namespace, crt.Spec.SecretName)

	if k8sErrors.IsNotFound(err) {
		signeeKey, err = pki.GenerateRSAPrivateKey(2048)
	}

	if err != nil {
		s := messageErrorGetCertKeyPair + err.Error()
		glog.Info(s)
		c.recorder.Event(update, v1.EventTypeWarning, errorGetCertKeyPair, s)
		update.UpdateStatusCondition(v1alpha1.CertificateConditionReady, v1alpha1.ConditionFalse, errorGetCertKeyPair, s)
		return update.Status, nil, nil, err
	}

	certPem, err := c.obtainCertificate(crt, &signeeKey.PublicKey)

	if err != nil {
		s := messageErrorIssueCert + err.Error()
		glog.Info(s)
		c.recorder.Event(update, v1.EventTypeWarning, errorIssueCert, s)
		update.UpdateStatusCondition(v1alpha1.CertificateConditionReady, v1alpha1.ConditionFalse, errorIssueCert, s)
		return update.Status, nil, nil, err
	}

	s := messageCertIssued
	glog.Info(s)
	c.recorder.Event(update, v1.EventTypeNormal, successCertIssued, s)
	update.UpdateStatusCondition(v1alpha1.CertificateConditionReady, v1alpha1.ConditionTrue, successCertIssued, s)

	return update.Status, pki.EncodePKCS1PrivateKey(signeeKey), certPem, nil
}

func (c *CA) obtainCertificate(crt *v1alpha1.Certificate, signeeKey interface{}) ([]byte, error) {
	signerCert, err := kube.SecretTLSCert(c.secretsLister, c.issuer.Namespace, c.issuer.Spec.CA.SecretRef.Name)

	if err != nil {
		return nil, fmt.Errorf("error getting issuer certificate: %s", err.Error())
	}

	signerKey, err := kube.SecretTLSKey(c.secretsLister, c.issuer.Namespace, c.issuer.Spec.CA.SecretRef.Name)

	if err != nil {
		return nil, fmt.Errorf("error getting issuer private key: %s", err.Error())
	}

	crtPem, _, err := signCertificate(crt, signerCert, signeeKey, signerKey)

	if err != nil {
		return nil, err
	}

	return crtPem, nil
}

func createCertificateTemplate(crt *v1alpha1.Certificate, publicKey interface{}) (*x509.Certificate, error) {
	serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
	serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
	if err != nil {
		return nil, fmt.Errorf("failed to generate serial number: %s", err.Error())
	}

	cert := &x509.Certificate{
		Version:               3,
		BasicConstraintsValid: true,
		SerialNumber:          serialNumber,
		SignatureAlgorithm:    x509.SHA256WithRSA,
		PublicKey:             publicKey,
		Subject: pkix.Name{
			Organization: []string{defaultOrganization},
			CommonName:   crt.Spec.Domains[0],
		},
		NotBefore: time.Now(),
		NotAfter:  time.Now().Add(certificateDuration),
		// see http://golang.org/pkg/crypto/x509/#KeyUsage
		KeyUsage: x509.KeyUsageDigitalSignature,
		DNSNames: crt.Spec.Domains,
	}
	return cert, nil
}

// signCertificate returns a signed x509.Certificate object for the given
// *v1alpha1.Certificate crt.
// publicKey is the public key of the signee, and signerKey is the private
// key of the signer.
func signCertificate(crt *v1alpha1.Certificate, issuerCert *x509.Certificate, publicKey interface{}, signerKey interface{}) ([]byte, *x509.Certificate, error) {
	template, err := createCertificateTemplate(crt, publicKey)
	if err != nil {
		return nil, nil, fmt.Errorf("error creating x509 certificate template: %s", err.Error())
	}

	derBytes, err := x509.CreateCertificate(rand.Reader, template, issuerCert, publicKey, signerKey)

	if err != nil {
		return nil, nil, fmt.Errorf("error creating x509 certificate: %s", err.Error())
	}

	cert, err := pki.DecodeDERCertificateBytes(derBytes)

	if err != nil {
		return nil, nil, fmt.Errorf("error decoding DER certificate bytes: %s", err.Error())
	}

	pemBytes := bytes.NewBuffer([]byte{})
	err = pem.Encode(pemBytes, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
	if err != nil {
		return nil, nil, fmt.Errorf("error encoding certificate PEM: %s", err.Error())
	}
	return pemBytes.Bytes(), cert, err
}
<|endoftext|>


<commit_before>package jsonnet

import (
	"io/ioutil"
	"path/filepath"
	"sort"

	jsonnet "github.com/google/go-jsonnet"
	"github.com/google/go-jsonnet/ast"
	"github.com/google/go-jsonnet/toolutils"
	"github.com/pkg/errors"

	"github.com/grafana/tanka/pkg/jsonnet/jpath"
	"github.com/grafana/tanka/pkg/jsonnet/native"
)

// TransitiveImports returns all recursive imports of an environment
func TransitiveImports(dir string) ([]string, error) {
	dir, err := filepath.Abs(dir)
	if err != nil {
		return nil, err
	}

	dir, err = filepath.EvalSymlinks(dir)
	if err != nil {
		return nil, err
	}

	mainFile := filepath.Join(dir, "main.jsonnet")

	sonnet, err := ioutil.ReadFile(mainFile)
	if err != nil {
		return nil, errors.Wrap(err, "opening file")
	}

	jpath, _, rootDir, err := jpath.Resolve(dir)
	if err != nil {
		return nil, errors.Wrap(err, "resolving JPATH")
	}

	vm := jsonnet.MakeVM()
	vm.Importer(NewExtendedImporter(jpath))
	for _, nf := range native.Funcs() {
		vm.NativeFunction(nf)
	}

	node, err := jsonnet.SnippetToAST("main.jsonnet", string(sonnet))
	if err != nil {
		return nil, errors.Wrap(err, "creating Jsonnet AST")
	}

	imports := make(map[string]bool)
	if err = importRecursive(imports, vm, node, "main.jsonnet"); err != nil {
		return nil, err
	}

	paths := make([]string, 0, len(imports)+1)
	for k := range imports {
		paths = append(paths, k)
	}
	paths = append(paths, mainFile)

	for i := range paths {
		paths[i], _ = filepath.Rel(rootDir, paths[i])
	}
	sort.Strings(paths)

	return paths, nil
}

// importRecursive takes a Jsonnet VM and recursively imports the AST. Every
// found import is added to the `list` string slice, which will ultimately
// contain all recursive imports
func importRecursive(list map[string]bool, vm *jsonnet.VM, node ast.Node, currentPath string) error {
	switch node := node.(type) {
	// we have an `import`
	case *ast.Import:
		p := node.File.Value

		contents, foundAt, err := vm.ImportAST(currentPath, p)
		if err != nil {
			return errors.Wrap(err, "importing jsonnet")
		}

		abs, _ := filepath.Abs(foundAt)
		if list[abs] {
			return nil
		}

		list[abs] = true

		if err := importRecursive(list, vm, contents, foundAt); err != nil {
			return err
		}

	// we have an `importstr`
	case *ast.ImportStr:
		p := node.File.Value

		foundAt, err := vm.ResolveImport(currentPath, p)
		if err != nil {
			return errors.Wrap(err, "importing string")
		}

		abs, _ := filepath.Abs(foundAt)
		if list[abs] {
			return nil
		}

		list[abs] = true

	// neither `import` nor `importstr`, probably object or similar: try children
	default:
		for _, child := range toolutils.Children(node) {
			if err := importRecursive(list, vm, child, currentPath); err != nil {
				return err
			}
		}
	}
	return nil
}

func uniqueStringSlice(s []string) []string {
	seen := make(map[string]struct{}, len(s))
	j := 0
	for _, v := range s {
		if _, ok := seen[v]; ok {
			continue
		}
		seen[v] = struct{}{}
		s[j] = v
		j++
	}
	return s[:j]
}
<commit_msg>fix(tool/imports): resolve dependency symlinks. (#303)<commit_after>package jsonnet

import (
	"io/ioutil"
	"path/filepath"
	"sort"

	jsonnet "github.com/google/go-jsonnet"
	"github.com/google/go-jsonnet/ast"
	"github.com/google/go-jsonnet/toolutils"
	"github.com/pkg/errors"

	"github.com/grafana/tanka/pkg/jsonnet/jpath"
	"github.com/grafana/tanka/pkg/jsonnet/native"
)

// TransitiveImports returns all recursive imports of an environment
func TransitiveImports(dir string) ([]string, error) {
	dir, err := filepath.Abs(dir)
	if err != nil {
		return nil, err
	}

	dir, err = filepath.EvalSymlinks(dir)
	if err != nil {
		return nil, err
	}

	mainFile := filepath.Join(dir, "main.jsonnet")

	sonnet, err := ioutil.ReadFile(mainFile)
	if err != nil {
		return nil, errors.Wrap(err, "opening file")
	}

	jpath, _, rootDir, err := jpath.Resolve(dir)
	if err != nil {
		return nil, errors.Wrap(err, "resolving JPATH")
	}

	vm := jsonnet.MakeVM()
	vm.Importer(NewExtendedImporter(jpath))
	for _, nf := range native.Funcs() {
		vm.NativeFunction(nf)
	}

	node, err := jsonnet.SnippetToAST("main.jsonnet", string(sonnet))
	if err != nil {
		return nil, errors.Wrap(err, "creating Jsonnet AST")
	}

	imports := make(map[string]bool)
	if err = importRecursive(imports, vm, node, "main.jsonnet"); err != nil {
		return nil, err
	}

	paths := make([]string, 0, len(imports)+1)
	for k := range imports {

		// Try to resolve any symlinks; use the original path as a last resort
		p, err := filepath.EvalSymlinks(k)
		if err != nil {
			return nil, errors.Wrap(err, "resolving symlinks")
		}
		paths = append(paths, p)

	}
	paths = append(paths, mainFile)

	for i := range paths {
		paths[i], _ = filepath.Rel(rootDir, paths[i])
	}
	sort.Strings(paths)

	return paths, nil
}

// importRecursive takes a Jsonnet VM and recursively imports the AST. Every
// found import is added to the `list` string slice, which will ultimately
// contain all recursive imports
func importRecursive(list map[string]bool, vm *jsonnet.VM, node ast.Node, currentPath string) error {
	switch node := node.(type) {
	// we have an `import`
	case *ast.Import:
		p := node.File.Value

		contents, foundAt, err := vm.ImportAST(currentPath, p)
		if err != nil {
			return errors.Wrap(err, "importing jsonnet")
		}

		abs, _ := filepath.Abs(foundAt)
		if list[abs] {
			return nil
		}

		list[abs] = true

		if err := importRecursive(list, vm, contents, foundAt); err != nil {
			return err
		}

	// we have an `importstr`
	case *ast.ImportStr:
		p := node.File.Value

		foundAt, err := vm.ResolveImport(currentPath, p)
		if err != nil {
			return errors.Wrap(err, "importing string")
		}

		abs, _ := filepath.Abs(foundAt)
		if list[abs] {
			return nil
		}

		list[abs] = true

	// neither `import` nor `importstr`, probably object or similar: try children
	default:
		for _, child := range toolutils.Children(node) {
			if err := importRecursive(list, vm, child, currentPath); err != nil {
				return err
			}
		}
	}
	return nil
}

func uniqueStringSlice(s []string) []string {
	seen := make(map[string]struct{}, len(s))
	j := 0
	for _, v := range s {
		if _, ok := seen[v]; ok {
			continue
		}
		seen[v] = struct{}{}
		s[j] = v
		j++
	}
	return s[:j]
}
<|endoftext|>


<commit_before>/*
Copyright 2014 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.
*/

// A set of common functions needed by cmd/kubectl and pkg/kubectl packages.
package kubectl

import (
	"errors"
	"fmt"
	"path"
	"strings"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/meta"
	"k8s.io/kubernetes/pkg/api/unversioned"
)

const (
	kubectlAnnotationPrefix = "kubectl.kubernetes.io/"
	// TODO: auto-generate this
	PossibleResourceTypes = `Possible resource types include (case insensitive): pods (aka 'po'), services (aka 'svc'), deployments (aka 'deploy'),
replicasets (aka 'rs'), replicationcontrollers (aka 'rc'), nodes (aka 'no'), events (aka 'ev'), limitranges (aka 'limits'),
persistentvolumes (aka 'pv'), persistentvolumeclaims (aka 'pvc'), resourcequotas (aka 'quota'), namespaces (aka 'ns'),
serviceaccounts (aka 'sa'), ingresses (aka 'ing'), horizontalpodautoscalers (aka 'hpa'), daemonsets (aka 'ds'), configmaps (aka 'cm'),
componentstatuses (aka 'cs), endpoints (aka 'ep'), petsets (alpha feature, may be unstable) and secrets.`
)

type NamespaceInfo struct {
	Namespace string
}

func listOfImages(spec *api.PodSpec) []string {
	images := make([]string, 0, len(spec.Containers))
	for _, container := range spec.Containers {
		images = append(images, container.Image)
	}
	return images
}

func makeImageList(spec *api.PodSpec) string {
	return strings.Join(listOfImages(spec), ",")
}

func NewThirdPartyResourceMapper(gvs []unversioned.GroupVersion, gvks []unversioned.GroupVersionKind) (meta.RESTMapper, error) {
	mapper := meta.NewDefaultRESTMapper(gvs, func(gv unversioned.GroupVersion) (*meta.VersionInterfaces, error) {
		for ix := range gvs {
			if gvs[ix].Group == gv.Group && gvs[ix].Version == gv.Version {
				return &meta.VersionInterfaces{
					ObjectConvertor:  api.Scheme,
					MetadataAccessor: meta.NewAccessor(),
				}, nil
			}
		}
		groupVersions := make([]string, 0, len(gvs))
		for ix := range gvs {
			groupVersions = append(groupVersions, gvs[ix].String())
		}
		return nil, fmt.Errorf("unsupported storage version: %s (valid: %s)", gv.String(), strings.Join(groupVersions, ", "))
	})
	for ix := range gvks {
		mapper.Add(gvks[ix], meta.RESTScopeNamespace)
	}
	return mapper, nil
}

// OutputVersionMapper is a RESTMapper that will prefer mappings that
// correspond to a preferred output version (if feasible)
type OutputVersionMapper struct {
	meta.RESTMapper

	// output versions takes a list of preferred GroupVersions. Only the first
	// hit for a given group will have effect.  This allows different output versions
	// depending upon the group of the kind being requested
	OutputVersions []unversioned.GroupVersion
}

// RESTMapping implements meta.RESTMapper by prepending the output version to the preferred version list.
func (m OutputVersionMapper) RESTMapping(gk unversioned.GroupKind, versions ...string) (*meta.RESTMapping, error) {
	for _, preferredVersion := range m.OutputVersions {
		if gk.Group == preferredVersion.Group {
			mapping, err := m.RESTMapper.RESTMapping(gk, preferredVersion.Version)
			if err == nil {
				return mapping, nil
			}

			break
		}
	}

	return m.RESTMapper.RESTMapping(gk, versions...)
}

// ShortcutExpander is a RESTMapper that can be used for Kubernetes
// resources.  It expands the resource first, then invokes the wrapped RESTMapper
type ShortcutExpander struct {
	RESTMapper meta.RESTMapper
}

var _ meta.RESTMapper = &ShortcutExpander{}

func (e ShortcutExpander) KindFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionKind, error) {
	return e.RESTMapper.KindFor(expandResourceShortcut(resource))
}

func (e ShortcutExpander) KindsFor(resource unversioned.GroupVersionResource) ([]unversioned.GroupVersionKind, error) {
	return e.RESTMapper.KindsFor(expandResourceShortcut(resource))
}

func (e ShortcutExpander) ResourcesFor(resource unversioned.GroupVersionResource) ([]unversioned.GroupVersionResource, error) {
	return e.RESTMapper.ResourcesFor(expandResourceShortcut(resource))
}

func (e ShortcutExpander) ResourceFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionResource, error) {
	return e.RESTMapper.ResourceFor(expandResourceShortcut(resource))
}

func (e ShortcutExpander) RESTMapping(gk unversioned.GroupKind, versions ...string) (*meta.RESTMapping, error) {
	return e.RESTMapper.RESTMapping(gk, versions...)
}

func (e ShortcutExpander) RESTMappings(gk unversioned.GroupKind) ([]*meta.RESTMapping, error) {
	return e.RESTMapper.RESTMappings(gk)
}

func (e ShortcutExpander) ResourceSingularizer(resource string) (string, error) {
	return e.RESTMapper.ResourceSingularizer(expandResourceShortcut(unversioned.GroupVersionResource{Resource: resource}).Resource)
}

func (e ShortcutExpander) AliasesForResource(resource string) ([]string, bool) {
	return e.RESTMapper.AliasesForResource(expandResourceShortcut(unversioned.GroupVersionResource{Resource: resource}).Resource)
}

// shortForms is the list of short names to their expanded names
var shortForms = map[string]string{
	// Please keep this alphabetized
	// If you add an entry here, please also take a look at pkg/kubectl/cmd/cmd.go
	// and add an entry to valid_resources when appropriate.
	"cm":     "configmaps",
	"cs":     "componentstatuses",
	"deploy": "deployments",
	"ds":     "daemonsets",
	"ep":     "endpoints",
	"ev":     "events",
	"hpa":    "horizontalpodautoscalers",
	"ing":    "ingresses",
	"limits": "limitranges",
	"no":     "nodes",
	"ns":     "namespaces",
	"po":     "pods",
	"psp":    "podSecurityPolicies",
	"pvc":    "persistentvolumeclaims",
	"pv":     "persistentvolumes",
	"quota":  "resourcequotas",
	"rc":     "replicationcontrollers",
	"rs":     "replicasets",
	"sa":     "serviceaccounts",
	"svc":    "services",
}

// Look-up for resource short forms by value
func ResourceShortFormFor(resource string) (string, bool) {
	var alias string
	exists := false
	for k, val := range shortForms {
		if val == resource {
			alias = k
			exists = true
		}
	}
	return alias, exists
}

// expandResourceShortcut will return the expanded version of resource
// (something that a pkg/api/meta.RESTMapper can understand), if it is
// indeed a shortcut. Otherwise, will return resource unmodified.
func expandResourceShortcut(resource unversioned.GroupVersionResource) unversioned.GroupVersionResource {
	if expanded, ok := shortForms[resource.Resource]; ok {
		// don't change the group or version that's already been specified
		resource.Resource = expanded
	}
	return resource
}

// ResourceAliases returns the resource shortcuts and plural forms for the given resources.
func ResourceAliases(rs []string) []string {
	as := make([]string, 0, len(rs))
	plurals := make(map[string]struct{}, len(rs))
	for _, r := range rs {
		var plural string
		switch {
		case r == "endpoints":
			plural = r // exception. "endpoint" does not exist. Why?
		case strings.HasSuffix(r, "y"):
			plural = r[0:len(r)-1] + "ies"
		case strings.HasSuffix(r, "s"):
			plural = r + "es"
		default:
			plural = r + "s"
		}
		as = append(as, plural)

		plurals[plural] = struct{}{}
	}

	for sf, r := range shortForms {
		if _, found := plurals[r]; found {
			as = append(as, sf)
		}
	}
	return as
}

// parseFileSource parses the source given. Acceptable formats include:
//
// 1.  source-path: the basename will become the key name
// 2.  source-name=source-path: the source-name will become the key name and source-path is the path to the key file
//
// Key names cannot include '='.
func parseFileSource(source string) (keyName, filePath string, err error) {
	numSeparators := strings.Count(source, "=")
	switch {
	case numSeparators == 0:
		return path.Base(source), source, nil
	case numSeparators == 1 && strings.HasPrefix(source, "="):
		return "", "", fmt.Errorf("key name for file path %v missing.", strings.TrimPrefix(source, "="))
	case numSeparators == 1 && strings.HasSuffix(source, "="):
		return "", "", fmt.Errorf("file path for key name %v missing.", strings.TrimSuffix(source, "="))
	case numSeparators > 1:
		return "", "", errors.New("Key names or file paths cannot contain '='.")
	default:
		components := strings.Split(source, "=")
		return components[0], components[1], nil
	}
}

// parseLiteralSource parses the source key=val pair
func parseLiteralSource(source string) (keyName, value string, err error) {
	// leading equal is invalid
	if strings.Index(source, "=") == 0 {
		return "", "", fmt.Errorf("invalid literal source %v, expected key=value", source)
	}
	// split after the first equal (so values can have the = character)
	items := strings.SplitN(source, "=", 2)
	if len(items) != 2 {
		return "", "", fmt.Errorf("invalid literal source %v, expected key=value", source)
	}

	return items[0], items[1], nil
}
<commit_msg>add shortname for certificate signing request<commit_after>/*
Copyright 2014 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.
*/

// A set of common functions needed by cmd/kubectl and pkg/kubectl packages.
package kubectl

import (
	"errors"
	"fmt"
	"path"
	"strings"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/meta"
	"k8s.io/kubernetes/pkg/api/unversioned"
)

const (
	kubectlAnnotationPrefix = "kubectl.kubernetes.io/"
	// TODO: auto-generate this
	PossibleResourceTypes = `Possible resource types include (case insensitive): pods (aka 'po'), services (aka 'svc'), deployments (aka 'deploy'),
replicasets (aka 'rs'), replicationcontrollers (aka 'rc'), nodes (aka 'no'), events (aka 'ev'), limitranges (aka 'limits'),
persistentvolumes (aka 'pv'), persistentvolumeclaims (aka 'pvc'), resourcequotas (aka 'quota'), namespaces (aka 'ns'),
serviceaccounts (aka 'sa'), ingresses (aka 'ing'), horizontalpodautoscalers (aka 'hpa'), daemonsets (aka 'ds'), configmaps (aka 'cm'),
componentstatuses (aka 'cs), endpoints (aka 'ep'), petsets (alpha feature, may be unstable) and secrets.`
)

type NamespaceInfo struct {
	Namespace string
}

func listOfImages(spec *api.PodSpec) []string {
	images := make([]string, 0, len(spec.Containers))
	for _, container := range spec.Containers {
		images = append(images, container.Image)
	}
	return images
}

func makeImageList(spec *api.PodSpec) string {
	return strings.Join(listOfImages(spec), ",")
}

func NewThirdPartyResourceMapper(gvs []unversioned.GroupVersion, gvks []unversioned.GroupVersionKind) (meta.RESTMapper, error) {
	mapper := meta.NewDefaultRESTMapper(gvs, func(gv unversioned.GroupVersion) (*meta.VersionInterfaces, error) {
		for ix := range gvs {
			if gvs[ix].Group == gv.Group && gvs[ix].Version == gv.Version {
				return &meta.VersionInterfaces{
					ObjectConvertor:  api.Scheme,
					MetadataAccessor: meta.NewAccessor(),
				}, nil
			}
		}
		groupVersions := make([]string, 0, len(gvs))
		for ix := range gvs {
			groupVersions = append(groupVersions, gvs[ix].String())
		}
		return nil, fmt.Errorf("unsupported storage version: %s (valid: %s)", gv.String(), strings.Join(groupVersions, ", "))
	})
	for ix := range gvks {
		mapper.Add(gvks[ix], meta.RESTScopeNamespace)
	}
	return mapper, nil
}

// OutputVersionMapper is a RESTMapper that will prefer mappings that
// correspond to a preferred output version (if feasible)
type OutputVersionMapper struct {
	meta.RESTMapper

	// output versions takes a list of preferred GroupVersions. Only the first
	// hit for a given group will have effect.  This allows different output versions
	// depending upon the group of the kind being requested
	OutputVersions []unversioned.GroupVersion
}

// RESTMapping implements meta.RESTMapper by prepending the output version to the preferred version list.
func (m OutputVersionMapper) RESTMapping(gk unversioned.GroupKind, versions ...string) (*meta.RESTMapping, error) {
	for _, preferredVersion := range m.OutputVersions {
		if gk.Group == preferredVersion.Group {
			mapping, err := m.RESTMapper.RESTMapping(gk, preferredVersion.Version)
			if err == nil {
				return mapping, nil
			}

			break
		}
	}

	return m.RESTMapper.RESTMapping(gk, versions...)
}

// ShortcutExpander is a RESTMapper that can be used for Kubernetes
// resources.  It expands the resource first, then invokes the wrapped RESTMapper
type ShortcutExpander struct {
	RESTMapper meta.RESTMapper
}

var _ meta.RESTMapper = &ShortcutExpander{}

func (e ShortcutExpander) KindFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionKind, error) {
	return e.RESTMapper.KindFor(expandResourceShortcut(resource))
}

func (e ShortcutExpander) KindsFor(resource unversioned.GroupVersionResource) ([]unversioned.GroupVersionKind, error) {
	return e.RESTMapper.KindsFor(expandResourceShortcut(resource))
}

func (e ShortcutExpander) ResourcesFor(resource unversioned.GroupVersionResource) ([]unversioned.GroupVersionResource, error) {
	return e.RESTMapper.ResourcesFor(expandResourceShortcut(resource))
}

func (e ShortcutExpander) ResourceFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionResource, error) {
	return e.RESTMapper.ResourceFor(expandResourceShortcut(resource))
}

func (e ShortcutExpander) RESTMapping(gk unversioned.GroupKind, versions ...string) (*meta.RESTMapping, error) {
	return e.RESTMapper.RESTMapping(gk, versions...)
}

func (e ShortcutExpander) RESTMappings(gk unversioned.GroupKind) ([]*meta.RESTMapping, error) {
	return e.RESTMapper.RESTMappings(gk)
}

func (e ShortcutExpander) ResourceSingularizer(resource string) (string, error) {
	return e.RESTMapper.ResourceSingularizer(expandResourceShortcut(unversioned.GroupVersionResource{Resource: resource}).Resource)
}

func (e ShortcutExpander) AliasesForResource(resource string) ([]string, bool) {
	return e.RESTMapper.AliasesForResource(expandResourceShortcut(unversioned.GroupVersionResource{Resource: resource}).Resource)
}

// shortForms is the list of short names to their expanded names
var shortForms = map[string]string{
	// Please keep this alphabetized
	// If you add an entry here, please also take a look at pkg/kubectl/cmd/cmd.go
	// and add an entry to valid_resources when appropriate.
	"cm":     "configmaps",
	"cs":     "componentstatuses",
	"csr":    "certificatesigningrequests",
	"deploy": "deployments",
	"ds":     "daemonsets",
	"ep":     "endpoints",
	"ev":     "events",
	"hpa":    "horizontalpodautoscalers",
	"ing":    "ingresses",
	"limits": "limitranges",
	"no":     "nodes",
	"ns":     "namespaces",
	"po":     "pods",
	"psp":    "podSecurityPolicies",
	"pvc":    "persistentvolumeclaims",
	"pv":     "persistentvolumes",
	"quota":  "resourcequotas",
	"rc":     "replicationcontrollers",
	"rs":     "replicasets",
	"sa":     "serviceaccounts",
	"svc":    "services",
}

// Look-up for resource short forms by value
func ResourceShortFormFor(resource string) (string, bool) {
	var alias string
	exists := false
	for k, val := range shortForms {
		if val == resource {
			alias = k
			exists = true
		}
	}
	return alias, exists
}

// expandResourceShortcut will return the expanded version of resource
// (something that a pkg/api/meta.RESTMapper can understand), if it is
// indeed a shortcut. Otherwise, will return resource unmodified.
func expandResourceShortcut(resource unversioned.GroupVersionResource) unversioned.GroupVersionResource {
	if expanded, ok := shortForms[resource.Resource]; ok {
		// don't change the group or version that's already been specified
		resource.Resource = expanded
	}
	return resource
}

// ResourceAliases returns the resource shortcuts and plural forms for the given resources.
func ResourceAliases(rs []string) []string {
	as := make([]string, 0, len(rs))
	plurals := make(map[string]struct{}, len(rs))
	for _, r := range rs {
		var plural string
		switch {
		case r == "endpoints":
			plural = r // exception. "endpoint" does not exist. Why?
		case strings.HasSuffix(r, "y"):
			plural = r[0:len(r)-1] + "ies"
		case strings.HasSuffix(r, "s"):
			plural = r + "es"
		default:
			plural = r + "s"
		}
		as = append(as, plural)

		plurals[plural] = struct{}{}
	}

	for sf, r := range shortForms {
		if _, found := plurals[r]; found {
			as = append(as, sf)
		}
	}
	return as
}

// parseFileSource parses the source given. Acceptable formats include:
//
// 1.  source-path: the basename will become the key name
// 2.  source-name=source-path: the source-name will become the key name and source-path is the path to the key file
//
// Key names cannot include '='.
func parseFileSource(source string) (keyName, filePath string, err error) {
	numSeparators := strings.Count(source, "=")
	switch {
	case numSeparators == 0:
		return path.Base(source), source, nil
	case numSeparators == 1 && strings.HasPrefix(source, "="):
		return "", "", fmt.Errorf("key name for file path %v missing.", strings.TrimPrefix(source, "="))
	case numSeparators == 1 && strings.HasSuffix(source, "="):
		return "", "", fmt.Errorf("file path for key name %v missing.", strings.TrimSuffix(source, "="))
	case numSeparators > 1:
		return "", "", errors.New("Key names or file paths cannot contain '='.")
	default:
		components := strings.Split(source, "=")
		return components[0], components[1], nil
	}
}

// parseLiteralSource parses the source key=val pair
func parseLiteralSource(source string) (keyName, value string, err error) {
	// leading equal is invalid
	if strings.Index(source, "=") == 0 {
		return "", "", fmt.Errorf("invalid literal source %v, expected key=value", source)
	}
	// split after the first equal (so values can have the = character)
	items := strings.SplitN(source, "=", 2)
	if len(items) != 2 {
		return "", "", fmt.Errorf("invalid literal source %v, expected key=value", source)
	}

	return items[0], items[1], nil
}
<|endoftext|>


<commit_before>// Copyright 2016-2020 The Libsacloud 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 mapconv

import (
	"errors"
	"reflect"
	"strings"

	"github.com/fatih/structs"
	"github.com/mitchellh/mapstructure"
)

// ConvertTo converts struct which input by mapconv to plain models
func ConvertTo(source interface{}, dest interface{}) error {
	s := structs.New(source)
	destMap := Map(make(map[string]interface{}))

	fields := s.Fields()
	for _, f := range fields {
		//if !f.IsExported() || f.IsZero() {
		if !f.IsExported() {
			continue
		}

		tags := ParseMapConvTag(f.Tag("mapconv"))
		for _, key := range tags.SourceFields {
			destKey := f.Name()
			value := f.Value()

			if key != "" {
				destKey = key
			}
			if f.IsZero() {
				if tags.OmitEmpty {
					continue
				}
				if tags.DefaultValue != nil {
					value = tags.DefaultValue
				}
			}

			if tags.Squash {
				d := Map(make(map[string]interface{}))
				err := ConvertTo(value, &d)
				if err != nil {
					return err
				}
				for k, v := range d {
					destMap.Set(k, v)
				}
				continue
			}

			if tags.Recursive {
				var dest []interface{}
				values := valueToSlice(value)
				for _, v := range values {
					if structs.IsStruct(v) {
						destMap := Map(make(map[string]interface{}))
						if err := ConvertTo(v, &destMap); err != nil {
							return err
						}
						dest = append(dest, destMap)
					} else {
						dest = append(dest, v)
					}
				}
				if tags.IsSlice || dest == nil || len(dest) > 1 {
					value = dest
				} else {
					value = dest[0]
				}
			}

			destMap.Set(destKey, value)
		}
	}

	config := &mapstructure.DecoderConfig{
		WeaklyTypedInput: true,
		Result:           dest,
	}
	decoder, err := mapstructure.NewDecoder(config)
	if err != nil {
		return err
	}
	return decoder.Decode(destMap.Map())
}

// ConvertFrom converts struct which input by mapconv from plain models
func ConvertFrom(source interface{}, dest interface{}) error {
	var sourceMap Map
	if m, ok := source.(map[string]interface{}); ok {
		sourceMap = Map(m)
	} else {
		sourceMap = Map(structs.New(source).Map())
	}
	destMap := Map(make(map[string]interface{}))

	s := structs.New(dest)
	fields := s.Fields()
	for _, f := range fields {
		if !f.IsExported() {
			continue
		}

		tags := ParseMapConvTag(f.Tag("mapconv"))
		if tags.Squash {
			return errors.New("ConvertFrom is not allowed squash")
		}
		for _, key := range tags.SourceFields {
			sourceKey := f.Name()
			if key != "" {
				sourceKey = key
			}

			value, err := sourceMap.Get(sourceKey)
			if err != nil {
				return err
			}
			if value == nil {
				continue
			}

			if tags.Recursive {
				t := reflect.TypeOf(f.Value())
				if t.Kind() == reflect.Slice {
					t = t.Elem().Elem()
				} else {
					t = t.Elem()
				}

				var dest []interface{}
				values := valueToSlice(value)
				for _, v := range values {
					if v == nil {
						dest = append(dest, v)
						continue
					}
					d := reflect.New(t).Interface()
					if err := ConvertFrom(v, d); err != nil {
						return err
					}
					dest = append(dest, d)
				}

				if dest != nil {
					if tags.IsSlice || len(dest) > 1 {
						value = dest
					} else {
						value = dest[0]
					}
				}
			}

			destMap.Set(f.Name(), value)
		}
	}
	config := &mapstructure.DecoderConfig{
		WeaklyTypedInput: true,
		Result:           dest,
	}
	decoder, err := mapstructure.NewDecoder(config)
	if err != nil {
		return err
	}
	return decoder.Decode(destMap.Map())
}

// TagInfo mapconvタグの情報
type TagInfo struct {
	SourceFields []string
	DefaultValue interface{}
	OmitEmpty    bool
	Recursive    bool
	Squash       bool
	IsSlice      bool
}

// ParseMapConvTag mapconvタグを文字列で受け取りパースしてTagInfoを返す
func ParseMapConvTag(tagBody string) TagInfo {
	tokens := strings.Split(tagBody, ",")
	key := tokens[0]

	keys := strings.Split(key, "/")
	var defaultValue interface{}
	var omitEmpty, recursive, squash, isSlice bool

	for _, k := range keys {
		if strings.Contains(k, "[]") {
			isSlice = true
		}
	}

	for i, token := range tokens {
		if i == 0 {
			continue
		}

		switch {
		case strings.HasPrefix(token, "omitempty"):
			omitEmpty = true
		case strings.HasPrefix(token, "recursive"):
			recursive = true
		case strings.HasPrefix(token, "squash"):
			squash = true
		case strings.HasPrefix(token, "default"):
			keyValue := strings.Split(token, "=")
			if len(keyValue) > 1 {
				defaultValue = strings.Join(keyValue[1:], "")
			}
		}
	}
	return TagInfo{
		SourceFields: keys,
		DefaultValue: defaultValue,
		OmitEmpty:    omitEmpty,
		Recursive:    recursive,
		Squash:       squash,
		IsSlice:      isSlice,
	}
}
<commit_msg>Fix nil checking<commit_after>// Copyright 2016-2020 The Libsacloud 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 mapconv

import (
	"errors"
	"reflect"
	"strings"

	"github.com/fatih/structs"
	"github.com/mitchellh/mapstructure"
)

// ConvertTo converts struct which input by mapconv to plain models
func ConvertTo(source interface{}, dest interface{}) error {
	s := structs.New(source)
	destMap := Map(make(map[string]interface{}))

	fields := s.Fields()
	for _, f := range fields {
		//if !f.IsExported() || f.IsZero() {
		if !f.IsExported() {
			continue
		}

		tags := ParseMapConvTag(f.Tag("mapconv"))
		for _, key := range tags.SourceFields {
			destKey := f.Name()
			value := f.Value()

			if key != "" {
				destKey = key
			}
			if f.IsZero() {
				if tags.OmitEmpty {
					continue
				}
				if tags.DefaultValue != nil {
					value = tags.DefaultValue
				}
			}

			if tags.Squash {
				d := Map(make(map[string]interface{}))
				err := ConvertTo(value, &d)
				if err != nil {
					return err
				}
				for k, v := range d {
					destMap.Set(k, v)
				}
				continue
			}

			if tags.Recursive {
				var dest []interface{}
				values := valueToSlice(value)
				for _, v := range values {
					if structs.IsStruct(v) {
						destMap := Map(make(map[string]interface{}))
						if err := ConvertTo(v, &destMap); err != nil {
							return err
						}
						dest = append(dest, destMap)
					} else {
						dest = append(dest, v)
					}
				}
				if tags.IsSlice || dest == nil || len(dest) > 1 {
					value = dest
				} else {
					value = dest[0]
				}
			}

			destMap.Set(destKey, value)
		}
	}

	config := &mapstructure.DecoderConfig{
		WeaklyTypedInput: true,
		Result:           dest,
	}
	decoder, err := mapstructure.NewDecoder(config)
	if err != nil {
		return err
	}
	return decoder.Decode(destMap.Map())
}

// ConvertFrom converts struct which input by mapconv from plain models
func ConvertFrom(source interface{}, dest interface{}) error {
	var sourceMap Map
	if m, ok := source.(map[string]interface{}); ok {
		sourceMap = Map(m)
	} else {
		sourceMap = Map(structs.New(source).Map())
	}
	destMap := Map(make(map[string]interface{}))

	s := structs.New(dest)
	fields := s.Fields()
	for _, f := range fields {
		if !f.IsExported() {
			continue
		}

		tags := ParseMapConvTag(f.Tag("mapconv"))
		if tags.Squash {
			return errors.New("ConvertFrom is not allowed squash")
		}
		for _, key := range tags.SourceFields {
			sourceKey := f.Name()
			if key != "" {
				sourceKey = key
			}

			value, err := sourceMap.Get(sourceKey)
			if err != nil {
				return err
			}
			if value == nil || reflect.ValueOf(value).IsZero() {
				continue
			}

			if tags.Recursive {
				t := reflect.TypeOf(f.Value())
				if t.Kind() == reflect.Slice {
					t = t.Elem().Elem()
				} else {
					t = t.Elem()
				}

				var dest []interface{}
				values := valueToSlice(value)
				for _, v := range values {
					if v == nil {
						dest = append(dest, v)
						continue
					}
					d := reflect.New(t).Interface()
					if err := ConvertFrom(v, d); err != nil {
						return err
					}
					dest = append(dest, d)
				}

				if dest != nil {
					if tags.IsSlice || len(dest) > 1 {
						value = dest
					} else {
						value = dest[0]
					}
				}
			}

			destMap.Set(f.Name(), value)
		}
	}
	config := &mapstructure.DecoderConfig{
		WeaklyTypedInput: true,
		Result:           dest,
	}
	decoder, err := mapstructure.NewDecoder(config)
	if err != nil {
		return err
	}
	return decoder.Decode(destMap.Map())
}

// TagInfo mapconvタグの情報
type TagInfo struct {
	SourceFields []string
	DefaultValue interface{}
	OmitEmpty    bool
	Recursive    bool
	Squash       bool
	IsSlice      bool
}

// ParseMapConvTag mapconvタグを文字列で受け取りパースしてTagInfoを返す
func ParseMapConvTag(tagBody string) TagInfo {
	tokens := strings.Split(tagBody, ",")
	key := tokens[0]

	keys := strings.Split(key, "/")
	var defaultValue interface{}
	var omitEmpty, recursive, squash, isSlice bool

	for _, k := range keys {
		if strings.Contains(k, "[]") {
			isSlice = true
		}
	}

	for i, token := range tokens {
		if i == 0 {
			continue
		}

		switch {
		case strings.HasPrefix(token, "omitempty"):
			omitEmpty = true
		case strings.HasPrefix(token, "recursive"):
			recursive = true
		case strings.HasPrefix(token, "squash"):
			squash = true
		case strings.HasPrefix(token, "default"):
			keyValue := strings.Split(token, "=")
			if len(keyValue) > 1 {
				defaultValue = strings.Join(keyValue[1:], "")
			}
		}
	}
	return TagInfo{
		SourceFields: keys,
		DefaultValue: defaultValue,
		OmitEmpty:    omitEmpty,
		Recursive:    recursive,
		Squash:       squash,
		IsSlice:      isSlice,
	}
}
<|endoftext|>


<commit_before>package project

var (
	description        = "The aws-operator manages Kubernetes clusters running on AWS."
	gitSHA             = "n/a"
	name        string = "aws-operator"
	source      string = "https://github.com/giantswarm/aws-operator"
	version            = "11.12.0"
)

func Description() string {
	return description
}

func GitSHA() string {
	return gitSHA
}

func Name() string {
	return name
}

func Source() string {
	return source
}

func Version() string {
	return version
}
<commit_msg>Bump version to 11.12.1-dev (#3342)<commit_after>package project

var (
	description        = "The aws-operator manages Kubernetes clusters running on AWS."
	gitSHA             = "n/a"
	name        string = "aws-operator"
	source      string = "https://github.com/giantswarm/aws-operator"
	version            = "11.12.1-dev"
)

func Description() string {
	return description
}

func GitSHA() string {
	return gitSHA
}

func Name() string {
	return name
}

func Source() string {
	return source
}

func Version() string {
	return version
}
<|endoftext|>


<commit_before>package testdb

import (
	r "github.com/dancannon/gorethink"
	"github.com/materials-commons/mcstore/pkg/db"
)

var session *r.Session

func RSession() *r.Session {
	if session == nil {
		session = db.RSessionUsingMust("localhost:30815", "mctestdb")
	}
	return session
}

// RSessionErr always returns a nil err. It will panic if it cannot
// get a db session. This function is meant to be used with the
// databaseSessionFilter for unit testing.
func RSessionErr() (*r.Session, error) {
	return RSession(), nil
}
<commit_msg>Don't cache the session since the databaseSessionFilter will close the session.<commit_after>package testdb

import (
	r "github.com/dancannon/gorethink"
	"github.com/materials-commons/mcstore/pkg/db"
)

func RSession() *r.Session {
	return db.RSessionUsingMust("localhost:30815", "mctestdb")
}

// RSessionErr always returns a nil err. It will panic if it cannot
// get a db session. This function is meant to be used with the
// databaseSessionFilter for unit testing.
func RSessionErr() (*r.Session, error) {
	return RSession(), nil
}
<|endoftext|>


<commit_before>package api_test

import (
	. "cf/api"
	"cf/configuration"
	"code.google.com/p/go.net/websocket"
	"code.google.com/p/gogoprotobuf/proto"
	"github.com/cloudfoundry/loggregatorlib/logmessage"
	. "github.com/onsi/ginkgo"
	"github.com/stretchr/testify/assert"
	mr "github.com/tjarratt/mr_t"
	"net/http/httptest"
	"strings"
	testapi "testhelpers/api"
	"time"
)

func init() {
	Describe("loggregator logs repository", func() {
		It("RecentLogsFor writes log messages onto the provided channel", func() {
			msg := marshalledLogMessageWithTime(mr.T(), "My message", int64(3000))
			expectedMessage, err := logmessage.ParseMessage(msg)
			assert.NoError(mr.T(), err)

			testServer, logsRepo := setupTestServerAndLogsRepo("/dump/", msg)
			defer testServer.Close()

			logChan := make(chan *logmessage.Message, 1000)
			err = logsRepo.RecentLogsFor("my-app-guid", func() {}, logChan)
			close(logChan)

			dumpedMessages := []*logmessage.Message{}
			for msg := range logChan {
				dumpedMessages = append(dumpedMessages, msg)
			}

			assert.NoError(mr.T(), err)
			assert.Equal(mr.T(), len(dumpedMessages), 1)
			assert.Equal(mr.T(), dumpedMessages[0].GetLogMessage().GetSourceName(), expectedMessage.GetLogMessage().GetSourceName())
			assert.Equal(mr.T(), dumpedMessages[0].GetLogMessage().GetMessage(), expectedMessage.GetLogMessage().GetMessage())
			assert.Equal(mr.T(), dumpedMessages[0].GetLogMessage().GetMessageType(), expectedMessage.GetLogMessage().GetMessageType())
		})

		It("TailLogsFor writes log messages on the channel in the correct order", func() {
			startTime := time.Now()
			messagesSent := [][]byte{
				marshalledLogMessageWithTime(mr.T(), "My message 1", startTime.UnixNano()),
				marshalledLogMessageWithTime(mr.T(), "My message 2", startTime.UnixNano()),
				marshalledLogMessageWithTime(mr.T(), "My message 3", startTime.UnixNano()),
			}

			testServer, logsRepo := setupTestServerAndLogsRepo("/tail/", messagesSent...)
			defer testServer.Close()

			logChan := make(chan *logmessage.Message, 1000)
			controlChan := make(chan bool)

			go func() {
				defer close(logChan)
				logsRepo.TailLogsFor("my-app-guid", func() {}, logChan, controlChan, time.Duration(1*time.Second))
			}()

			var messages []string
			for msg := range logChan {
				messages = append(messages, string(msg.GetLogMessage().Message))
			}

			assert.Equal(mr.T(), len(messages), 3)
			assert.Equal(mr.T(), messages, []string{"My message 1", "My message 2", "My message 3"})
		})
	})
}

func setupTestServerAndLogsRepo(requestURI string, messages ...[]byte) (testServer *httptest.Server, logsRepo *LoggregatorLogsRepository) {
	handler := func(conn *websocket.Conn) {
		request := conn.Request()
		assert.Equal(mr.T(), request.URL.Path, requestURI)
		assert.Equal(mr.T(), request.URL.RawQuery, "app=my-app-guid")
		assert.Equal(mr.T(), request.Method, "GET")
		assert.Contains(mr.T(), request.Header.Get("Authorization"), "BEARER my_access_token")

		for _, msg := range messages {
			conn.Write(msg)
		}
		time.Sleep(time.Duration(50) * time.Millisecond)
		conn.Close()
	}
	testServer = httptest.NewTLSServer(websocket.Handler(handler))

	config := &configuration.Configuration{AccessToken: "BEARER my_access_token", Target: "https://localhost"}
	endpointRepo := &testapi.FakeEndpointRepo{}
	endpointRepo.LoggregatorEndpointReturns.Endpoint = strings.Replace(testServer.URL, "https", "wss", 1)

	repo := NewLoggregatorLogsRepository(config, endpointRepo)
	logsRepo = &repo
	return
}

func marshalledLogMessageWithTime(t mr.TestingT, messageString string, timestamp int64) []byte {
	messageType := logmessage.LogMessage_OUT
	sourceName := "DEA"
	protoMessage := &logmessage.LogMessage{
		Message:     []byte(messageString),
		AppId:       proto.String("my-app-guid"),
		MessageType: &messageType,
		SourceName:  &sourceName,
		Timestamp:   proto.Int64(timestamp),
	}

	message, err := proto.Marshal(protoMessage)
	assert.NoError(t, err)

	return message
}
<commit_msg>BDD-ify logs_repo tests<commit_after>package api_test

import (
	. "cf/api"
	"cf/configuration"
	"code.google.com/p/go.net/websocket"
	"code.google.com/p/gogoprotobuf/proto"
	"github.com/cloudfoundry/loggregatorlib/logmessage"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"net/http/httptest"
	"strings"
	testapi "testhelpers/api"
	"time"
)

var _ = Describe("loggregator logs repository", func() {

	var (
		logChan        chan *logmessage.Message
		testServer     *httptest.Server
		requestHandler *requestHandlerWithExpectedPath
		logsRepo       *LoggregatorLogsRepository
		messagesToSend [][]byte
	)

	BeforeEach(func() {
		startTime := time.Now()
		messagesToSend = [][]byte{
			marshalledLogMessageWithTime("My message 1", startTime.UnixNano()),
			marshalledLogMessageWithTime("My message 2", startTime.UnixNano()),
			marshalledLogMessageWithTime("My message 3", startTime.UnixNano()),
		}
		logChan = make(chan *logmessage.Message, 1000)
		testServer, requestHandler, logsRepo = setupTestServerAndLogsRepo(messagesToSend...)
	})

	AfterEach(func() {
		testServer.Close()
	})

	Describe("RecentLogsFor", func() {

		BeforeEach(func() {
			err := logsRepo.RecentLogsFor("my-app-guid", func() {}, logChan)
			Expect(err).NotTo(HaveOccurred())
			close(logChan)
		})

		It("connects to the dump endpoint", func() {
			Expect(requestHandler.lastPath).To(Equal("/dump/"))
		})

		It("writes log messages onto the provided channel", func() {
			dumpedMessages := []*logmessage.Message{}
			for msg := range logChan {
				dumpedMessages = append(dumpedMessages, msg)
			}

			Expect(len(dumpedMessages)).To(Equal(3))
			Expect(dumpedMessages[0]).To(Equal(parseMessage(messagesToSend[0])))
			Expect(dumpedMessages[1]).To(Equal(parseMessage(messagesToSend[1])))
			Expect(dumpedMessages[2]).To(Equal(parseMessage(messagesToSend[2])))
		})
	})

	Describe("TailLogsFor", func() {

		BeforeEach(func() {
			err := logsRepo.TailLogsFor("my-app-guid", func() {}, logChan, make(chan bool), time.Duration(1*time.Second))
			Expect(err).NotTo(HaveOccurred())
			close(logChan)
		})

		It("connects to the tailing endpoint", func() {
			Expect(requestHandler.lastPath).To(Equal("/tail/"))
		})

		It("writes log messages on the channel in the correct order", func() {
			var messages []string
			for msg := range logChan {
				messages = append(messages, string(msg.GetLogMessage().Message))
			}

			Expect(messages).To(Equal([]string{"My message 1", "My message 2", "My message 3"}))
		})
	})
})

func parseMessage(msgBytes []byte) (msg *logmessage.Message) {
	msg, err := logmessage.ParseMessage(msgBytes)
	Expect(err).ToNot(HaveOccurred())
	return
}

type requestHandlerWithExpectedPath struct {
	handlerFunc func(conn *websocket.Conn)
	lastPath    string
}

func setupTestServerAndLogsRepo(messages ...[]byte) (testServer *httptest.Server, requestHandler *requestHandlerWithExpectedPath, logsRepo *LoggregatorLogsRepository) {
	requestHandler = new(requestHandlerWithExpectedPath)
	requestHandler.handlerFunc = func(conn *websocket.Conn) {
		request := conn.Request()
		requestHandler.lastPath = request.URL.Path
		Expect(request.URL.RawQuery).To(Equal("app=my-app-guid"))
		Expect(request.Method).To(Equal("GET"))
		Expect(request.Header.Get("Authorization")).To(ContainSubstring("BEARER my_access_token"))

		for _, msg := range messages {
			conn.Write(msg)
		}
		time.Sleep(time.Duration(50) * time.Millisecond)
		conn.Close()
	}

	testServer = httptest.NewTLSServer(websocket.Handler(requestHandler.handlerFunc))

	config := &configuration.Configuration{AccessToken: "BEARER my_access_token", Target: "https://localhost"}
	endpointRepo := &testapi.FakeEndpointRepo{}
	endpointRepo.LoggregatorEndpointReturns.Endpoint = strings.Replace(testServer.URL, "https", "wss", 1)

	repo := NewLoggregatorLogsRepository(config, endpointRepo)
	logsRepo = &repo
	return
}

func marshalledLogMessageWithTime(messageString string, timestamp int64) []byte {
	messageType := logmessage.LogMessage_OUT
	sourceName := "DEA"
	protoMessage := &logmessage.LogMessage{
		Message:     []byte(messageString),
		AppId:       proto.String("my-app-guid"),
		MessageType: &messageType,
		SourceName:  &sourceName,
		Timestamp:   proto.Int64(timestamp),
	}

	message, err := proto.Marshal(protoMessage)
	Expect(err).ToNot(HaveOccurred())

	return message
}
<|endoftext|>


<commit_before>// Copyright 2016, RadiantBlue Technologies, Inc.
//
// 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 pzsvc

import (
	"bytes"
	"crypto/rand"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"runtime"
	"strings"
	"time"
)

// LogFunc is the function used to add entries to the log

func baseLogFunc(logString string) {
	fmt.Println(logString)
}

var (
	// LogFunc is the logging function in use by this instance.
	// It exists as a way to easily control where your logs go.
	LogFunc = baseLogFunc
)

// LoggedError is a duplicate of the "error" interface.  Its real point is to
// indicate, when it is returned from a function, that the error it represents
// has already been entered into the log and does not need to be entered again.
// The string contained in the LoggedError should be a relatively simple
// description of the error, suitable for returning to the caller of a REST
// interface.
type LoggedError error

// logMessage receives a string to put to the logs.  It formats it correctly
// and puts it in the right place.  This function exists partially in order
// to simplify the task of modifying log behavior in the future.
func logMessage(s Session, prefix, message string) {
	_, file, line, _ := runtime.Caller(2)
	if s.LogRootDir != "" {
		splits := strings.SplitAfter(file, s.LogRootDir)
		if len(splits) > 1 {
			file = s.LogRootDir + splits[len(splits)-1]
		}
	}
	outMsg := fmt.Sprintf("%s - [%s:%s %s %d] %s", prefix, s.AppName, s.SessionID, file, line, message)
	LogFunc(outMsg)
}

// LogSimpleErr posts a logMessage call for simple error messages, and produces a pzsvc.Error
// from the result.  The point is mostly to maintain uniformity of appearance and behavior.
func LogSimpleErr(s Session, message string, err error) LoggedError {
	if err != nil {
		message += err.Error()
	}
	logMessage(s, "ERROR", message)
	return fmt.Errorf(message)
}

// LogInfo posts a logMessage call for standard, non-error messages.  The
// point is mostly to maintain uniformity of appearance and behavior.
func LogInfo(s Session, message string) {
	logMessage(s, "INFO", message)
}

// LogWarn posts a logMessage call for messages that suggest that something
// may be going wrong, especially in the case of expected user error, but
// that the program can make reasonable assumptions as to what was actually
// intended and carry on.  The point of this function is mostly to maintain
// uniformity of appearance and behavior.
func LogWarn(s Session, message string) {
	logMessage(s, "WARN", message)
}

// LogAlert posts a logMessage call for messages that suggest that someone
// may be attempting to breach the security of the program, or point to the
// possibility of a significant security vulnerability.  The point of this
// function is mostly to maintain uniformity of appearance and behavior.
func LogAlert(s Session, message string) {
	logMessage(s, "ALERT", message)
}

// various constants representing the levels of severity for a given audit message
const (
	FATAL    = 0
	CRITICAL = 2
	ERROR    = 3
	WARN     = 4
	NOTICE   = 5
	INFO     = 6
	DEBUG    = 7
)

// LogAudit posts a logMessage call for messages that are generated to
// conform to Audit requirements.  This function is intended to maintain
// uniformity of appearance and behavior, and also to ease maintainability
// when routing requirements change.
func LogAudit(s Session, actor, action, actee, msg string, severity int) {
	if s.LogAudit {
		time := time.Now().UTC().Format("2006-01-02T15:04:05.999Z")

		hostName, _ := os.Hostname()
		outStr := fmt.Sprintf(`<%d>1 %s %s %s - ID%d [pzaudit@48851 actor="%s" action="%s" actee="%s"] %s`,
			8+severity, time, hostName, s.AppName, os.Getpid(), actor, action, actee, msg)
		LogFunc(outStr)
		//logMessage(s, "AUDIT", actor+": "+action+": "+actee)
	}
}

// LogAuditResponse is LogAudit for those cases where it needs to include an HTTP response
// body, and that body is not beign conveniently read and outputted by some other function.
// It reads the response, logs the result, and replaces the consumed response body with a
// fresh one made from the read buffer, so that it doesn't interfere with any other function
// that woudl wish to access the body.
func LogAuditResponse(s Session, actor, action, actee string, resp *http.Response, severity int) {
	if s.LogAudit {
		bbuff, _ := ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		resp.Body = ioutil.NopCloser(bytes.NewBuffer(bbuff))
		trimPld := strings.Replace(string(bbuff), "\n", "", -1)
		LogAudit(s, actor, action, actee, trimPld, severity)
	}
}

// Error is intended as a somewhat more full-featured way of handlign the
// error niche
type Error struct {
	hasLogged  bool   // whether or not this Error has been logged
	LogMsg     string // message to enter into logs
	SimpleMsg  string // simplified message to return to user via rest endpoint
	request    string // http request body associated with the error (if any)
	response   string // http response body assocaited with the error (if any)
	url        string // url associated with the error (if any)
	httpStatus int    // http status associated with the error (if any)
}

// OverwriteRequest exists because some requests contain auth information.  For security
// reasons, that needs to be stripped out before logging, but the logic that knows
// it needs to be removed only exists outside of the pzsvc library.
func (err Error) OverwriteRequest(inReq string) {
	err.request = inReq
}

// GenExtendedMsg is used to generate extended log messages from Error objects
// for the cases where that's appropriate
func (err Error) GenExtendedMsg() string {
	lineBreak := "\n/**************************************/\n"
	outBody := "Http Error: " + err.LogMsg + lineBreak
	if err.url != "" {
		outBody += "\nURL: " + err.url + "\n"
	}
	if err.request != "" {
		outBody += "\nRequest: " + err.request + "\n"
	}
	if err.response != "" {
		outBody += "\nResponse: " + err.response + "\n"
	}
	if http.StatusText(err.httpStatus) != "" {
		outBody += "\nHTTP Status: " + http.StatusText(err.httpStatus) + "\n"
	}
	outBody += lineBreak
	return outBody
}

// Log is intended as the base way to generate logging information for an Error
// object.  It constructs an extended error if necessary, gathers the filename
// and line number data, and sends it to logMessage for formatting and output.
// It also ensures that any given error will only be logged once, and will be
// logged at the lowest level that calls for it.  In particular, the general
// expectation is that the message will be generated at a relatively low level,
// and then logged with additional context at some higher position.  Given our
// general level of complexity, that strikes a decent balance between providing
// enough detail to figure out the cause of an error and keepign thigns simple
// enough to readily understand.
func (err *Error) Log(s Session, msgAdd string) LoggedError {
	if !err.hasLogged {
		if msgAdd != "" {
			err.LogMsg = msgAdd + ": " + err.LogMsg
		}
		outMsg := err.LogMsg
		if err.request != "" || err.response != "" {
			outMsg = err.GenExtendedMsg()
		}
		logMessage(s, "ERROR", outMsg)
		err.hasLogged = true
	} else {
		logMessage(s, "ERROR", "Meta-error.  Tried to log same message for a second time.")
	}
	if err.SimpleMsg != "" {
		return fmt.Errorf(err.SimpleMsg)
	}
	return fmt.Errorf(err.LogMsg)

}

// SliceToCommaSep takes a string slice, and turns it into a comma-separated
// list of strings, suitable for JSON.
func SliceToCommaSep(inSlice []string) string {
	sliLen := len(inSlice)
	if sliLen == 0 {
		return ""
	}
	accum := inSlice[0]
	for i := 1; i < sliLen; i++ {
		accum = accum + "," + inSlice[i]
	}
	return accum
}

// PsuUUID makes a psuedo-UUID.  It may not achieve cryptographic levels of
// randomness, and it won't respond properly to standard ways of pulling data
// out of UUIDs, but it works just fine at generating effectively unique IDs
// for practical purposes.
func PsuUUID() (string, error) {
	b := make([]byte, 16)
	_, err := rand.Read(b)
	if err != nil {
		return "", err
	}

	return fmt.Sprintf("%X-%X-%X-%X-%X", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]), nil
}
<commit_msg>bugfix<commit_after>// Copyright 2016, RadiantBlue Technologies, Inc.
//
// 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 pzsvc

import (
	"bytes"
	"crypto/rand"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"runtime"
	"strings"
	"time"
)

// LogFunc is the function used to add entries to the log

func baseLogFunc(logString string) {
	fmt.Println(logString)
}

var (
	// LogFunc is the logging function in use by this instance.
	// It exists as a way to easily control where your logs go.
	LogFunc = baseLogFunc
)

// LoggedError is a duplicate of the "error" interface.  Its real point is to
// indicate, when it is returned from a function, that the error it represents
// has already been entered into the log and does not need to be entered again.
// The string contained in the LoggedError should be a relatively simple
// description of the error, suitable for returning to the caller of a REST
// interface.
type LoggedError error

// logMessage receives a string to put to the logs.  It formats it correctly
// and puts it in the right place.  This function exists partially in order
// to simplify the task of modifying log behavior in the future.
func logMessage(s Session, prefix, message string) {
	_, file, line, _ := runtime.Caller(2)
	if s.LogRootDir != "" {
		splits := strings.SplitAfter(file, s.LogRootDir)
		if len(splits) > 1 {
			file = s.LogRootDir + splits[len(splits)-1]
		}
	}
	outMsg := fmt.Sprintf("%s - [%s:%s %s %d] %s", prefix, s.AppName, s.SessionID, file, line, message)
	LogFunc(outMsg)
}

// LogSimpleErr posts a logMessage call for simple error messages, and produces a pzsvc.Error
// from the result.  The point is mostly to maintain uniformity of appearance and behavior.
func LogSimpleErr(s Session, message string, err error) LoggedError {
	if err != nil {
		message += err.Error()
	}
	logMessage(s, "ERROR", message)
	return fmt.Errorf(message)
}

// LogInfo posts a logMessage call for standard, non-error messages.  The
// point is mostly to maintain uniformity of appearance and behavior.
func LogInfo(s Session, message string) {
	logMessage(s, "INFO", message)
}

// LogWarn posts a logMessage call for messages that suggest that something
// may be going wrong, especially in the case of expected user error, but
// that the program can make reasonable assumptions as to what was actually
// intended and carry on.  The point of this function is mostly to maintain
// uniformity of appearance and behavior.
func LogWarn(s Session, message string) {
	logMessage(s, "WARN", message)
}

// LogAlert posts a logMessage call for messages that suggest that someone
// may be attempting to breach the security of the program, or point to the
// possibility of a significant security vulnerability.  The point of this
// function is mostly to maintain uniformity of appearance and behavior.
func LogAlert(s Session, message string) {
	logMessage(s, "ALERT", message)
}

// various constants representing the levels of severity for a given audit message
const (
	FATAL    = 0
	CRITICAL = 2
	ERROR    = 3
	WARN     = 4
	NOTICE   = 5
	INFO     = 6
	DEBUG    = 7
)

// LogAudit posts a logMessage call for messages that are generated to
// conform to Audit requirements.  This function is intended to maintain
// uniformity of appearance and behavior, and also to ease maintainability
// when routing requirements change.
func LogAudit(s Session, actor, action, actee, msg string, severity int) {
	if s.LogAudit {
		time := time.Now().UTC().Format("2006-01-02T15:04:05.999Z")

		hostName, _ := os.Hostname()
		outStr := fmt.Sprintf(`<%d>1 %s %s %s - ID%d [pzaudit@48851 actor="%s" action="%s" actee="%s"] %s`,
			8+severity, time, hostName, s.AppName, os.Getpid(), actor, action, actee, msg)
		LogFunc(outStr)
		//logMessage(s, "AUDIT", actor+": "+action+": "+actee)
	}
}

// LogAuditResponse is LogAudit for those cases where it needs to include an HTTP response
// body, and that body is not beign conveniently read and outputted by some other function.
// It reads the response, logs the result, and replaces the consumed response body with a
// fresh one made from the read buffer, so that it doesn't interfere with any other function
// that woudl wish to access the body.
func LogAuditResponse(s Session, actor, action, actee string, resp *http.Response, severity int) {
	if s.LogAudit {
		bbuff, _ := ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		resp.Body = ioutil.NopCloser(bytes.NewBuffer(bbuff))
		trimPld := strings.Replace(string(bbuff), "\n", "", -1)
		LogAudit(s, actor, action, actee, trimPld, severity)
	}
}

// Error is intended as a somewhat more full-featured way of handlign the
// error niche
type Error struct {
	hasLogged  bool   // whether or not this Error has been logged
	LogMsg     string // message to enter into logs
	SimpleMsg  string // simplified message to return to user via rest endpoint
	request    string // http request body associated with the error (if any)
	response   string // http response body assocaited with the error (if any)
	url        string // url associated with the error (if any)
	httpStatus int    // http status associated with the error (if any)
}

// OverwriteRequest exists because some requests contain auth information.  For security
// reasons, that needs to be stripped out before logging, but the logic that knows
// it needs to be removed only exists outside of the pzsvc library.
func (err *Error) OverwriteRequest(inReq string) {
	err.request = inReq
}

// GenExtendedMsg is used to generate extended log messages from Error objects
// for the cases where that's appropriate
func (err Error) GenExtendedMsg() string {
	lineBreak := "\n/**************************************/\n"
	outBody := "Http Error: " + err.LogMsg + lineBreak
	if err.url != "" {
		outBody += "\nURL: " + err.url + "\n"
	}
	if err.request != "" {
		outBody += "\nRequest: " + err.request + "\n"
	}
	if err.response != "" {
		outBody += "\nResponse: " + err.response + "\n"
	}
	if http.StatusText(err.httpStatus) != "" {
		outBody += "\nHTTP Status: " + http.StatusText(err.httpStatus) + "\n"
	}
	outBody += lineBreak
	return outBody
}

// Log is intended as the base way to generate logging information for an Error
// object.  It constructs an extended error if necessary, gathers the filename
// and line number data, and sends it to logMessage for formatting and output.
// It also ensures that any given error will only be logged once, and will be
// logged at the lowest level that calls for it.  In particular, the general
// expectation is that the message will be generated at a relatively low level,
// and then logged with additional context at some higher position.  Given our
// general level of complexity, that strikes a decent balance between providing
// enough detail to figure out the cause of an error and keepign thigns simple
// enough to readily understand.
func (err *Error) Log(s Session, msgAdd string) LoggedError {
	if !err.hasLogged {
		if msgAdd != "" {
			err.LogMsg = msgAdd + ": " + err.LogMsg
		}
		outMsg := err.LogMsg
		if err.request != "" || err.response != "" {
			outMsg = err.GenExtendedMsg()
		}
		logMessage(s, "ERROR", outMsg)
		err.hasLogged = true
	} else {
		logMessage(s, "ERROR", "Meta-error.  Tried to log same message for a second time.")
	}
	if err.SimpleMsg != "" {
		return fmt.Errorf(err.SimpleMsg)
	}
	return fmt.Errorf(err.LogMsg)

}

// SliceToCommaSep takes a string slice, and turns it into a comma-separated
// list of strings, suitable for JSON.
func SliceToCommaSep(inSlice []string) string {
	sliLen := len(inSlice)
	if sliLen == 0 {
		return ""
	}
	accum := inSlice[0]
	for i := 1; i < sliLen; i++ {
		accum = accum + "," + inSlice[i]
	}
	return accum
}

// PsuUUID makes a psuedo-UUID.  It may not achieve cryptographic levels of
// randomness, and it won't respond properly to standard ways of pulling data
// out of UUIDs, but it works just fine at generating effectively unique IDs
// for practical purposes.
func PsuUUID() (string, error) {
	b := make([]byte, 16)
	_, err := rand.Read(b)
	if err != nil {
		return "", err
	}

	return fmt.Sprintf("%X-%X-%X-%X-%X", b[0:4], b[4:6], b[6:8], b[8:10], b[10:]), nil
}
<|endoftext|>


<commit_before>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by
// license that can be found in the LICENSE file.

// Package daemon windows version
package daemon

import (
	"errors"
	"fmt"
	"os/exec"
	"syscall"
	"unicode/utf16"
	"unsafe"
)

var ErrWindowsUnsupported = errors.New("Adding as a service failed. Download and place nssm.exe in the path to install this service as an service. NSSM url: https://nssm.cc/")

// windowsRecord - standard record (struct) for windows version of daemon package
type windowsRecord struct {
	name         string
	description  string
	dependencies []string
}

func newDaemon(name, description string, dependencies []string) (Daemon, error) {

	return &windowsRecord{name, description, dependencies}, nil
}

// Install the service
func (windows *windowsRecord) Install(args ...string) (string, error) {
	installAction := "Install " + windows.description + ":"
	adminAccessNecessary := "Administrator access is needed to install a service."

	execp, err := execPath()

	if err != nil {
		return installAction + failed, err
	}

	cmd := exec.Command("nssm.exe", "install", windows.name, execp)
	out, err := cmd.Output()
	if err != nil {
		if len(out) > 0 {
			fmt.Println(string(out))
		} else {
			fmt.Println("No output. Probably service already exists. Try uninstall first.")
		}
		return installAction + failed, err
	}
	if len(out) == 0 {
		return adminAccessNecessary, errors.New(adminAccessNecessary)
	}
	return installAction + " completed.", nil
}

// Remove the service
func (windows *windowsRecord) Remove() (string, error) {
	removeAction := "Removing " + windows.description + ":"
	cmd := exec.Command("nssm.exe", "remove", windows.name)
	err := cmd.Run()
	if err != nil {
		return removeAction + failed, err
	}
	return removeAction + " completed.", nil
}

// Start the service
func (windows *windowsRecord) Start() (string, error) {
	startAction := "Starting " + windows.description + ":"
	cmd := exec.Command("nssm.exe", "start", windows.name)
	err := cmd.Run()
	if err != nil {
		return startAction + failed, err
	}
	return startAction + " completed.", nil
}

// Stop the service
func (windows *windowsRecord) Stop() (string, error) {
	stopAction := "Stopping " + windows.description + ":"
	cmd := exec.Command("nssm.exe", "stop", windows.name)
	err := cmd.Run()
	if err != nil {
		return stopAction + failed, err
	}
	return stopAction + " completed.", nil
}

// Status - Get service status
func (windows *windowsRecord) Status() (string, error) {
	cmd := exec.Command("nssm.exe", "status", windows.name)
	out, err := cmd.Output()
	if err != nil {
		return "Getting status:" + failed, err
	}
	return "Status: " + string(out), nil
}

// Get executable path
func execPath() (string, error) {
	var n uint32
	b := make([]uint16, syscall.MAX_PATH)
	size := uint32(len(b))

	r0, _, e1 := syscall.MustLoadDLL(
		"kernel32.dll",
	).MustFindProc(
		"GetModuleFileNameW",
	).Call(0, uintptr(unsafe.Pointer(&b[0])), uintptr(size))
	n = uint32(r0)
	if n == 0 {
		return "", e1
	}
	return string(utf16.Decode(b[0:n])), nil
}
<commit_msg>windows, remove a service without confirmation<commit_after>// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by
// license that can be found in the LICENSE file.

// Package daemon windows version
package daemon

import (
	"errors"
	"fmt"
	"os/exec"
	"syscall"
	"unicode/utf16"
	"unsafe"
)

var ErrWindowsUnsupported = errors.New("Adding as a service failed. Download and place nssm.exe in the path to install this service as an service. NSSM url: https://nssm.cc/")

// windowsRecord - standard record (struct) for windows version of daemon package
type windowsRecord struct {
	name         string
	description  string
	dependencies []string
}

func newDaemon(name, description string, dependencies []string) (Daemon, error) {

	return &windowsRecord{name, description, dependencies}, nil
}

// Install the service
func (windows *windowsRecord) Install(args ...string) (string, error) {
	installAction := "Install " + windows.description + ":"
	adminAccessNecessary := "Administrator access is needed to install a service."

	execp, err := execPath()

	if err != nil {
		return installAction + failed, err
	}

	cmd := exec.Command("nssm.exe", "install", windows.name, execp)
	out, err := cmd.Output()
	if err != nil {
		if len(out) > 0 {
			fmt.Println(string(out))
		} else {
			fmt.Println("No output. Probably service already exists. Try uninstall first.")
		}
		return installAction + failed, err
	}
	if len(out) == 0 {
		return adminAccessNecessary, errors.New(adminAccessNecessary)
	}
	return installAction + " completed.", nil
}

// Remove the service
func (windows *windowsRecord) Remove() (string, error) {
	removeAction := "Removing " + windows.description + ":"
	cmd := exec.Command("nssm.exe", "remove", windows.name, "confirm")
	err := cmd.Run()
	if err != nil {
		return removeAction + failed, err
	}
	return removeAction + " completed.", nil
}

// Start the service
func (windows *windowsRecord) Start() (string, error) {
	startAction := "Starting " + windows.description + ":"
	cmd := exec.Command("nssm.exe", "start", windows.name)
	err := cmd.Run()
	if err != nil {
		return startAction + failed, err
	}
	return startAction + " completed.", nil
}

// Stop the service
func (windows *windowsRecord) Stop() (string, error) {
	stopAction := "Stopping " + windows.description + ":"
	cmd := exec.Command("nssm.exe", "stop", windows.name)
	err := cmd.Run()
	if err != nil {
		return stopAction + failed, err
	}
	return stopAction + " completed.", nil
}

// Status - Get service status
func (windows *windowsRecord) Status() (string, error) {
	cmd := exec.Command("nssm.exe", "status", windows.name)
	out, err := cmd.Output()
	if err != nil {
		return "Getting status:" + failed, err
	}
	return "Status: " + string(out), nil
}

// Get executable path
func execPath() (string, error) {
	var n uint32
	b := make([]uint16, syscall.MAX_PATH)
	size := uint32(len(b))

	r0, _, e1 := syscall.MustLoadDLL(
		"kernel32.dll",
	).MustFindProc(
		"GetModuleFileNameW",
	).Call(0, uintptr(unsafe.Pointer(&b[0])), uintptr(size))
	n = uint32(r0)
	if n == 0 {
		return "", e1
	}
	return string(utf16.Decode(b[0:n])), nil
}
<|endoftext|>