<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package awstasks

import (
	"fmt"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/golang/glog"
	"k8s.io/kops/upup/pkg/fi"
	"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
	"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
)

//go:generate fitask -type=NatGateway
type NatGateway struct {
	Name      *string
	ElasticIP *ElasticIP
	Subnet    *Subnet
	ID        *string

	EgressId *string

	// Shared is set if this is a shared NatGateway
	Shared *bool

	// We can't tag NatGateways, so we have to find through a surrogate
	AssociatedRouteTable *RouteTable
}

var _ fi.CompareWithID = &NatGateway{} // Validate the IDs

func (e *NatGateway) CompareWithID() *string {
	return e.ID
}

func (e *NatGateway) Find(c *fi.Context) (*NatGateway, error) {

	cloud := c.Cloud.(awsup.AWSCloud)
	var ngw *ec2.NatGateway
	actual := &NatGateway{}

	if e.ID != nil && *e.ID != "" {
		// We have an existing NGW, lets look up the EIP
		var ngwIds []*string
		ngwIds = append(ngwIds, e.ID)

		request := &ec2.DescribeNatGatewaysInput{
			NatGatewayIds: ngwIds,
		}

		response, err := cloud.EC2().DescribeNatGateways(request)

		if err != nil {
			return nil, fmt.Errorf("error listing Nat Gateways %v", err)
		}

		if len(response.NatGateways) != 1 {
			return nil, fmt.Errorf("found %b NAT Gateways, expected 1", len(response.NatGateways))
		}
		if len(response.NatGateways) == 1 {
			ngw = response.NatGateways[0]
		}

		if len(response.NatGateways[0].NatGatewayAddresses) != 1 {
			return nil, fmt.Errorf("found %b EIP Addresses for 1 NAT Gateway, expected 1", len(response.NatGateways))
		}
		if len(response.NatGateways[0].NatGatewayAddresses) == 1 {

			actual.ElasticIP = &ElasticIP{ID: response.NatGateways[0].NatGatewayAddresses[0].AllocationId}

		}
	} else {
		// This is the normal/default path
		ngw, err := e.findNatGateway(c)
		if err != nil {
			return nil, err
		}
		if ngw == nil {
			return nil, nil
		}
	}

	actual.ID = ngw.NatGatewayId

	actual.Subnet = e.Subnet
	if len(ngw.NatGatewayAddresses) == 0 {
		// Not sure if this ever happens
		actual.ElasticIP = nil
	} else if len(ngw.NatGatewayAddresses) == 1 {
		actual.ElasticIP = &ElasticIP{ID: ngw.NatGatewayAddresses[0].AllocationId}
	} else {
		return nil, fmt.Errorf("found multiple elastic IPs attached to NatGateway %q", aws.StringValue(ngw.NatGatewayId))
	}

	// NATGateways don't have a Name (no tags), so we set the name to avoid spurious changes
	actual.Name = e.Name

	actual.AssociatedRouteTable = e.AssociatedRouteTable

	e.ID = actual.ID
	return actual, nil
}

func (e *NatGateway) findNatGateway(c *fi.Context) (*ec2.NatGateway, error) {
	cloud := c.Cloud.(awsup.AWSCloud)

	id := e.ID

	// Find via route on private route table
	if id == nil && e.AssociatedRouteTable != nil {
		ngw, err := findNatGatewayFromRouteTable(cloud, e.AssociatedRouteTable)
		if err != nil {
			return nil, err
		}
		if ngw != nil {
			return ngw, nil
		}
	}

	// Find via tag on subnet
	// TODO: Obsolete - we can get from the route table instead
	if id == nil && e.Subnet != nil {
		var filters []*ec2.Filter
		filters = append(filters, awsup.NewEC2Filter("key", "AssociatedNatgateway"))
		if e.Subnet.ID == nil {
			glog.V(2).Infof("Unable to find subnet, bypassing Find() for NatGateway")
			return nil, nil
		}
		filters = append(filters, awsup.NewEC2Filter("resource-id", *e.Subnet.ID))

		request := &ec2.DescribeTagsInput{
			Filters: filters,
		}

		response, err := cloud.EC2().DescribeTags(request)
		if err != nil {
			return nil, fmt.Errorf("error listing tags: %v", err)
		}

		if response == nil || len(response.Tags) == 0 {
			return nil, nil
		}

		if len(response.Tags) != 1 {
			return nil, fmt.Errorf("found multiple tags for: %v", e)
		}
		t := response.Tags[0]
		id = t.Value
		glog.V(2).Infof("Found NatGateway via subnet tag: %v", *id)
	}

	if id != nil {
		return findNatGatewayById(cloud, id)
	}

	return nil, nil
}

func findNatGatewayById(cloud awsup.AWSCloud, id *string) (*ec2.NatGateway, error) {
	request := &ec2.DescribeNatGatewaysInput{}
	request.NatGatewayIds = []*string{id}
	response, err := cloud.EC2().DescribeNatGateways(request)
	if err != nil {
		return nil, fmt.Errorf("error listing NatGateway %q: %v", id, err)
	}

	if response == nil || len(response.NatGateways) == 0 {
		glog.V(2).Infof("Unable to find NatGateway %q", id)
		return nil, nil
	}
	if len(response.NatGateways) != 1 {
		return nil, fmt.Errorf("found multiple NatGateways with id %q", id)
	}
	return response.NatGateways[0], nil
}

func findNatGatewayFromRouteTable(cloud awsup.AWSCloud, routeTable *RouteTable) (*ec2.NatGateway, error) {
	// Find via route on private route table
	if routeTable.ID != nil {
		glog.V(2).Infof("trying to match NatGateway via RouteTable %s", routeTable.ID)
		rt, err := routeTable.findEc2RouteTable(cloud)
		if err != nil {
			return nil, fmt.Errorf("error finding associated RouteTable to NatGateway: %v", err)
		}

		if rt != nil {
			var natGatewayIDs []*string
			for _, route := range rt.Routes {
				if route.NatGatewayId != nil {
					natGatewayIDs = append(natGatewayIDs, route.NatGatewayId)
				}
			}

			if len(natGatewayIDs) == 0 {
				glog.V(2).Infof("no NatGateway found in route table %s", *rt.RouteTableId)
			} else if len(natGatewayIDs) > 1 {
				return nil, fmt.Errorf("found multiple NatGateways in route table %s", *rt.RouteTableId)
			} else {
				return findNatGatewayById(cloud, natGatewayIDs[0])
			}
		}
	}

	return nil, nil
}

func (s *NatGateway) CheckChanges(a, e, changes *NatGateway) error {
	// New
	if a == nil {
		if e.ElasticIP == nil {
			return fi.RequiredField("ElasticIp")
		}
		if e.Subnet == nil {
			return fi.RequiredField("Subnet")
		}
	}

	// Delta
	if a != nil {
		if changes.ElasticIP != nil {
			return fi.CannotChangeField("ElasticIp")
		}
		if changes.Subnet != nil {
			return fi.CannotChangeField("Subnet")
		}
		if changes.ID != nil {
			return fi.CannotChangeField("ID")
		}
	}
	return nil
}

func (e *NatGateway) Run(c *fi.Context) error {
	return fi.DefaultDeltaRunMethod(e, c)
}

func (e *NatGateway) waitAvailable(cloud awsup.AWSCloud) error {
	// It takes 'forever' (up to 5 min...) for a NatGateway to become available after it has been created
	// We have to wait until it is actually up

	// TODO: Cache availability status

	id := aws.StringValue(e.ID)
	if id == "" {
		return fmt.Errorf("NAT Gateway %q did not have ID", e.Name)
	}

	glog.Infof("Waiting for NAT Gateway %q to be available", id)
	params := &ec2.DescribeNatGatewaysInput{
		NatGatewayIds: []*string{e.ID},
	}
	err := cloud.EC2().WaitUntilNatGatewayAvailable(params)
	if err != nil {
		return fmt.Errorf("error waiting for NAT Gateway %q to be available: %v", id, err)
	}

	return nil
}

func (_ *NatGateway) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *NatGateway) error {
	// New NGW

	var id *string
	if a == nil {
		glog.V(2).Infof("Creating Nat Gateway")

		request := &ec2.CreateNatGatewayInput{}
		request.AllocationId = e.ElasticIP.ID
		request.SubnetId = e.Subnet.ID
		response, err := t.Cloud.EC2().CreateNatGateway(request)
		if err != nil {
			return fmt.Errorf("Error creating Nat Gateway: %v", err)
		}
		e.ID = response.NatGateway.NatGatewayId
		id = e.ID
	} else {
		id = a.ID
	}

	// Tag the associated subnet
	if e.Subnet == nil {
		return fmt.Errorf("Subnet not set")
	} else if e.Subnet.ID == nil {
		return fmt.Errorf("Subnet ID not set")
	}

	// TODO: Obsolete - we can get from the route table instead
	tags := make(map[string]string)
	tags["AssociatedNatgateway"] = *id
	err := t.AddAWSTags(*e.Subnet.ID, tags)
	if err != nil {
		return fmt.Errorf("unable to tag subnet %v", err)
	}

	// If this is a shared NGW, we need to tag it
	// The tag that implies "shared" is `AssociatedNatgateway`=> NGW-ID
	// This is better than just a tag that's shared because this lets us create a whitelist of these NGWs
	// without doing a bunch more work in `kutil/delete_cluster.go`

	if *e.Shared == true {
		glog.V(2).Infof("tagging route table %s to track shared NGW", *e.AssociatedRouteTable.ID)
		err = t.AddAWSTags(*e.AssociatedRouteTable.ID, tags)
	}

	return nil
}

type terraformNATGateway struct {
	AllocationID *terraform.Literal `json:"allocation_id,omitempty"`
	SubnetID     *terraform.Literal `json:"subnet_id,omitempty"`
}

func (_ *NatGateway) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *NatGateway) error {
	tf := &terraformNATGateway{
		AllocationID: e.ElasticIP.TerraformLink(),
		SubnetID:     e.Subnet.TerraformLink(),
	}

	return t.RenderResource("aws_nat_gateway", *e.Name, tf)
}

func (e *NatGateway) TerraformLink() *terraform.Literal {
	return terraform.LiteralProperty("aws_nat_gateway", *e.Name, "id")
}
<commit_msg>Add message about expected time for NAT gateway creation<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package awstasks

import (
	"fmt"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/golang/glog"
	"k8s.io/kops/upup/pkg/fi"
	"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
	"k8s.io/kops/upup/pkg/fi/cloudup/terraform"
)

//go:generate fitask -type=NatGateway
type NatGateway struct {
	Name      *string
	ElasticIP *ElasticIP
	Subnet    *Subnet
	ID        *string

	EgressId *string

	// Shared is set if this is a shared NatGateway
	Shared *bool

	// We can't tag NatGateways, so we have to find through a surrogate
	AssociatedRouteTable *RouteTable
}

var _ fi.CompareWithID = &NatGateway{} // Validate the IDs

func (e *NatGateway) CompareWithID() *string {
	return e.ID
}

func (e *NatGateway) Find(c *fi.Context) (*NatGateway, error) {

	cloud := c.Cloud.(awsup.AWSCloud)
	var ngw *ec2.NatGateway
	actual := &NatGateway{}

	if e.ID != nil && *e.ID != "" {
		// We have an existing NGW, lets look up the EIP
		var ngwIds []*string
		ngwIds = append(ngwIds, e.ID)

		request := &ec2.DescribeNatGatewaysInput{
			NatGatewayIds: ngwIds,
		}

		response, err := cloud.EC2().DescribeNatGateways(request)

		if err != nil {
			return nil, fmt.Errorf("error listing Nat Gateways %v", err)
		}

		if len(response.NatGateways) != 1 {
			return nil, fmt.Errorf("found %b NAT Gateways, expected 1", len(response.NatGateways))
		}
		if len(response.NatGateways) == 1 {
			ngw = response.NatGateways[0]
		}

		if len(response.NatGateways[0].NatGatewayAddresses) != 1 {
			return nil, fmt.Errorf("found %b EIP Addresses for 1 NAT Gateway, expected 1", len(response.NatGateways))
		}
		if len(response.NatGateways[0].NatGatewayAddresses) == 1 {

			actual.ElasticIP = &ElasticIP{ID: response.NatGateways[0].NatGatewayAddresses[0].AllocationId}

		}
	} else {
		// This is the normal/default path
		ngw, err := e.findNatGateway(c)
		if err != nil {
			return nil, err
		}
		if ngw == nil {
			return nil, nil
		}
	}

	actual.ID = ngw.NatGatewayId

	actual.Subnet = e.Subnet
	if len(ngw.NatGatewayAddresses) == 0 {
		// Not sure if this ever happens
		actual.ElasticIP = nil
	} else if len(ngw.NatGatewayAddresses) == 1 {
		actual.ElasticIP = &ElasticIP{ID: ngw.NatGatewayAddresses[0].AllocationId}
	} else {
		return nil, fmt.Errorf("found multiple elastic IPs attached to NatGateway %q", aws.StringValue(ngw.NatGatewayId))
	}

	// NATGateways don't have a Name (no tags), so we set the name to avoid spurious changes
	actual.Name = e.Name

	actual.AssociatedRouteTable = e.AssociatedRouteTable

	e.ID = actual.ID
	return actual, nil
}

func (e *NatGateway) findNatGateway(c *fi.Context) (*ec2.NatGateway, error) {
	cloud := c.Cloud.(awsup.AWSCloud)

	id := e.ID

	// Find via route on private route table
	if id == nil && e.AssociatedRouteTable != nil {
		ngw, err := findNatGatewayFromRouteTable(cloud, e.AssociatedRouteTable)
		if err != nil {
			return nil, err
		}
		if ngw != nil {
			return ngw, nil
		}
	}

	// Find via tag on subnet
	// TODO: Obsolete - we can get from the route table instead
	if id == nil && e.Subnet != nil {
		var filters []*ec2.Filter
		filters = append(filters, awsup.NewEC2Filter("key", "AssociatedNatgateway"))
		if e.Subnet.ID == nil {
			glog.V(2).Infof("Unable to find subnet, bypassing Find() for NatGateway")
			return nil, nil
		}
		filters = append(filters, awsup.NewEC2Filter("resource-id", *e.Subnet.ID))

		request := &ec2.DescribeTagsInput{
			Filters: filters,
		}

		response, err := cloud.EC2().DescribeTags(request)
		if err != nil {
			return nil, fmt.Errorf("error listing tags: %v", err)
		}

		if response == nil || len(response.Tags) == 0 {
			return nil, nil
		}

		if len(response.Tags) != 1 {
			return nil, fmt.Errorf("found multiple tags for: %v", e)
		}
		t := response.Tags[0]
		id = t.Value
		glog.V(2).Infof("Found NatGateway via subnet tag: %v", *id)
	}

	if id != nil {
		return findNatGatewayById(cloud, id)
	}

	return nil, nil
}

func findNatGatewayById(cloud awsup.AWSCloud, id *string) (*ec2.NatGateway, error) {
	request := &ec2.DescribeNatGatewaysInput{}
	request.NatGatewayIds = []*string{id}
	response, err := cloud.EC2().DescribeNatGateways(request)
	if err != nil {
		return nil, fmt.Errorf("error listing NatGateway %q: %v", id, err)
	}

	if response == nil || len(response.NatGateways) == 0 {
		glog.V(2).Infof("Unable to find NatGateway %q", id)
		return nil, nil
	}
	if len(response.NatGateways) != 1 {
		return nil, fmt.Errorf("found multiple NatGateways with id %q", id)
	}
	return response.NatGateways[0], nil
}

func findNatGatewayFromRouteTable(cloud awsup.AWSCloud, routeTable *RouteTable) (*ec2.NatGateway, error) {
	// Find via route on private route table
	if routeTable.ID != nil {
		glog.V(2).Infof("trying to match NatGateway via RouteTable %s", routeTable.ID)
		rt, err := routeTable.findEc2RouteTable(cloud)
		if err != nil {
			return nil, fmt.Errorf("error finding associated RouteTable to NatGateway: %v", err)
		}

		if rt != nil {
			var natGatewayIDs []*string
			for _, route := range rt.Routes {
				if route.NatGatewayId != nil {
					natGatewayIDs = append(natGatewayIDs, route.NatGatewayId)
				}
			}

			if len(natGatewayIDs) == 0 {
				glog.V(2).Infof("no NatGateway found in route table %s", *rt.RouteTableId)
			} else if len(natGatewayIDs) > 1 {
				return nil, fmt.Errorf("found multiple NatGateways in route table %s", *rt.RouteTableId)
			} else {
				return findNatGatewayById(cloud, natGatewayIDs[0])
			}
		}
	}

	return nil, nil
}

func (s *NatGateway) CheckChanges(a, e, changes *NatGateway) error {
	// New
	if a == nil {
		if e.ElasticIP == nil {
			return fi.RequiredField("ElasticIp")
		}
		if e.Subnet == nil {
			return fi.RequiredField("Subnet")
		}
	}

	// Delta
	if a != nil {
		if changes.ElasticIP != nil {
			return fi.CannotChangeField("ElasticIp")
		}
		if changes.Subnet != nil {
			return fi.CannotChangeField("Subnet")
		}
		if changes.ID != nil {
			return fi.CannotChangeField("ID")
		}
	}
	return nil
}

func (e *NatGateway) Run(c *fi.Context) error {
	return fi.DefaultDeltaRunMethod(e, c)
}

func (e *NatGateway) waitAvailable(cloud awsup.AWSCloud) error {
	// It takes 'forever' (up to 5 min...) for a NatGateway to become available after it has been created
	// We have to wait until it is actually up

	// TODO: Cache availability status

	id := aws.StringValue(e.ID)
	if id == "" {
		return fmt.Errorf("NAT Gateway %q did not have ID", e.Name)
	}

	glog.Infof("Waiting for NAT Gateway %q to be available (this often takes about 5 minutes)", id)
	params := &ec2.DescribeNatGatewaysInput{
		NatGatewayIds: []*string{e.ID},
	}
	err := cloud.EC2().WaitUntilNatGatewayAvailable(params)
	if err != nil {
		return fmt.Errorf("error waiting for NAT Gateway %q to be available: %v", id, err)
	}

	return nil
}

func (_ *NatGateway) RenderAWS(t *awsup.AWSAPITarget, a, e, changes *NatGateway) error {
	// New NGW

	var id *string
	if a == nil {
		glog.V(2).Infof("Creating Nat Gateway")

		request := &ec2.CreateNatGatewayInput{}
		request.AllocationId = e.ElasticIP.ID
		request.SubnetId = e.Subnet.ID
		response, err := t.Cloud.EC2().CreateNatGateway(request)
		if err != nil {
			return fmt.Errorf("Error creating Nat Gateway: %v", err)
		}
		e.ID = response.NatGateway.NatGatewayId
		id = e.ID
	} else {
		id = a.ID
	}

	// Tag the associated subnet
	if e.Subnet == nil {
		return fmt.Errorf("Subnet not set")
	} else if e.Subnet.ID == nil {
		return fmt.Errorf("Subnet ID not set")
	}

	// TODO: Obsolete - we can get from the route table instead
	tags := make(map[string]string)
	tags["AssociatedNatgateway"] = *id
	err := t.AddAWSTags(*e.Subnet.ID, tags)
	if err != nil {
		return fmt.Errorf("unable to tag subnet %v", err)
	}

	// If this is a shared NGW, we need to tag it
	// The tag that implies "shared" is `AssociatedNatgateway`=> NGW-ID
	// This is better than just a tag that's shared because this lets us create a whitelist of these NGWs
	// without doing a bunch more work in `kutil/delete_cluster.go`

	if *e.Shared == true {
		glog.V(2).Infof("tagging route table %s to track shared NGW", *e.AssociatedRouteTable.ID)
		err = t.AddAWSTags(*e.AssociatedRouteTable.ID, tags)
	}

	return nil
}

type terraformNATGateway struct {
	AllocationID *terraform.Literal `json:"allocation_id,omitempty"`
	SubnetID     *terraform.Literal `json:"subnet_id,omitempty"`
}

func (_ *NatGateway) RenderTerraform(t *terraform.TerraformTarget, a, e, changes *NatGateway) error {
	tf := &terraformNATGateway{
		AllocationID: e.ElasticIP.TerraformLink(),
		SubnetID:     e.Subnet.TerraformLink(),
	}

	return t.RenderResource("aws_nat_gateway", *e.Name, tf)
}

func (e *NatGateway) TerraformLink() *terraform.Literal {
	return terraform.LiteralProperty("aws_nat_gateway", *e.Name, "id")
}
<|endoftext|>


<commit_before>// Copyright 2018 Prometheus Team
// 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 cli

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

	"github.com/pkg/errors"
	kingpin "gopkg.in/alecthomas/kingpin.v2"

	"github.com/prometheus/alertmanager/api/v2/client/silence"
	"github.com/prometheus/alertmanager/api/v2/models"
)

type silenceImportCmd struct {
	force   bool
	workers int
	file    string
}

const silenceImportHelp = `Import alertmanager silences from JSON file or stdin

This command can be used to bulk import silences from a JSON file
created by query command. For example:

amtool silence query -o json foo > foo.json

amtool silence import foo.json

JSON data can also come from stdin if no param is specified.
`

func configureSilenceImportCmd(cc *kingpin.CmdClause) {
	var (
		c         = &silenceImportCmd{}
		importCmd = cc.Command("import", silenceImportHelp)
	)

	importCmd.Flag("force", "Force adding new silences even if it already exists").Short('f').BoolVar(&c.force)
	importCmd.Flag("worker", "Number of concurrent workers to use for import").Short('w').Default("8").IntVar(&c.workers)
	importCmd.Arg("input-file", "JSON file with silences").ExistingFileVar(&c.file)
	importCmd.Action(execWithTimeout(c.bulkImport))
}

func addSilenceWorker(ctx context.Context, sclient *silence.Client, silencec <-chan *models.PostableSilence, errc chan<- error) {
	for s := range silencec {
		sid := s.ID
		params := silence.NewPostSilencesParams().WithContext(ctx).WithSilence(s)
		postOk, err := sclient.PostSilences(params)
		if err != nil && strings.Contains(err.Error(), "not found") {
			// silence doesn't exists yet, retry to create as a new one
			params.Silence.ID = ""
			postOk, err = sclient.PostSilences(params)
		}

		if err != nil {
			fmt.Fprintf(os.Stderr, "Error adding silence id='%v': %v\n", sid, err)
		} else {
			fmt.Println(postOk.Payload.SilenceID)
		}
		errc <- err
	}
}

func (c *silenceImportCmd) bulkImport(ctx context.Context, _ *kingpin.ParseContext) error {
	input := os.Stdin
	var err error
	if c.file != "" {
		input, err = os.Open(c.file)
		if err != nil {
			return err
		}
		defer input.Close()
	}

	dec := json.NewDecoder(input)
	// read open square bracket
	_, err = dec.Token()
	if err != nil {
		return errors.Wrap(err, "couldn't unmarshal input data, is it JSON?")
	}

	amclient := NewAlertmanagerClient(alertmanagerURL)
	silencec := make(chan *models.PostableSilence, 100)
	errc := make(chan error, 100)
	var wg sync.WaitGroup
	for w := 0; w < c.workers; w++ {
		wg.Add(1)
		go func() {
			addSilenceWorker(ctx, amclient.Silence, silencec, errc)
			wg.Done()
		}()
	}

	errCount := 0
	go func() {
		for err := range errc {
			if err != nil {
				errCount++
			}
		}
	}()

	count := 0
	for dec.More() {
		var s models.PostableSilence
		err := dec.Decode(&s)
		if err != nil {
			return errors.Wrap(err, "couldn't unmarshal input data, is it JSON?")
		}

		if c.force {
			// reset the silence ID so Alertmanager will always create new silence
			s.ID = ""
		}

		silencec <- &s
		count++
	}

	close(silencec)
	wg.Wait()
	close(errc)

	if errCount > 0 {
		return fmt.Errorf("couldn't import %v out of %v silences", errCount, count)
	}
	return nil
}
<commit_msg>cli: check for NotFound error during silence import<commit_after>// Copyright 2018 Prometheus Team
// 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 cli

import (
	"context"
	"encoding/json"
	"fmt"
	"os"
	"sync"

	"github.com/pkg/errors"
	kingpin "gopkg.in/alecthomas/kingpin.v2"

	"github.com/prometheus/alertmanager/api/v2/client/silence"
	"github.com/prometheus/alertmanager/api/v2/models"
)

type silenceImportCmd struct {
	force   bool
	workers int
	file    string
}

const silenceImportHelp = `Import alertmanager silences from JSON file or stdin

This command can be used to bulk import silences from a JSON file
created by query command. For example:

amtool silence query -o json foo > foo.json

amtool silence import foo.json

JSON data can also come from stdin if no param is specified.
`

func configureSilenceImportCmd(cc *kingpin.CmdClause) {
	var (
		c         = &silenceImportCmd{}
		importCmd = cc.Command("import", silenceImportHelp)
	)

	importCmd.Flag("force", "Force adding new silences even if it already exists").Short('f').BoolVar(&c.force)
	importCmd.Flag("worker", "Number of concurrent workers to use for import").Short('w').Default("8").IntVar(&c.workers)
	importCmd.Arg("input-file", "JSON file with silences").ExistingFileVar(&c.file)
	importCmd.Action(execWithTimeout(c.bulkImport))
}

func addSilenceWorker(ctx context.Context, sclient *silence.Client, silencec <-chan *models.PostableSilence, errc chan<- error) {
	for s := range silencec {
		sid := s.ID
		params := silence.NewPostSilencesParams().WithContext(ctx).WithSilence(s)
		postOk, err := sclient.PostSilences(params)
		if _, ok := err.(*silence.PostSilencesNotFound); ok {
			// silence doesn't exists yet, retry to create as a new one
			params.Silence.ID = ""
			postOk, err = sclient.PostSilences(params)
		}

		if err != nil {
			fmt.Fprintf(os.Stderr, "Error adding silence id='%v': %v\n", sid, err)
		} else {
			fmt.Println(postOk.Payload.SilenceID)
		}
		errc <- err
	}
}

func (c *silenceImportCmd) bulkImport(ctx context.Context, _ *kingpin.ParseContext) error {
	input := os.Stdin
	var err error
	if c.file != "" {
		input, err = os.Open(c.file)
		if err != nil {
			return err
		}
		defer input.Close()
	}

	dec := json.NewDecoder(input)
	// read open square bracket
	_, err = dec.Token()
	if err != nil {
		return errors.Wrap(err, "couldn't unmarshal input data, is it JSON?")
	}

	amclient := NewAlertmanagerClient(alertmanagerURL)
	silencec := make(chan *models.PostableSilence, 100)
	errc := make(chan error, 100)
	var wg sync.WaitGroup
	for w := 0; w < c.workers; w++ {
		wg.Add(1)
		go func() {
			addSilenceWorker(ctx, amclient.Silence, silencec, errc)
			wg.Done()
		}()
	}

	errCount := 0
	go func() {
		for err := range errc {
			if err != nil {
				errCount++
			}
		}
	}()

	count := 0
	for dec.More() {
		var s models.PostableSilence
		err := dec.Decode(&s)
		if err != nil {
			return errors.Wrap(err, "couldn't unmarshal input data, is it JSON?")
		}

		if c.force {
			// reset the silence ID so Alertmanager will always create new silence
			s.ID = ""
		}

		silencec <- &s
		count++
	}

	close(silencec)
	wg.Wait()
	close(errc)

	if errCount > 0 {
		return fmt.Errorf("couldn't import %v out of %v silences", errCount, count)
	}
	return nil
}
<|endoftext|>


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

package http

import (
	"encoding/json"
	"fmt"
	"reflect"
	"testing"
	"time"
)

var writeSetCookiesTests = []struct {
	Cookie *Cookie
	Raw    string
}{
	{
		&Cookie{Name: "cookie-1", Value: "v$1"},
		"cookie-1=v$1",
	},
	{
		&Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600},
		"cookie-2=two; Max-Age=3600",
	},
	{
		&Cookie{Name: "cookie-3", Value: "three", Domain: ".example.com"},
		"cookie-3=three; Domain=.example.com",
	},
	{
		&Cookie{Name: "cookie-4", Value: "four", Path: "/restricted/"},
		"cookie-4=four; Path=/restricted/",
	},
}

func TestWriteSetCookies(t *testing.T) {
	for i, tt := range writeSetCookiesTests {
		if g, e := tt.Cookie.String(), tt.Raw; g != e {
			t.Errorf("Test %d, expecting:\n%s\nGot:\n%s\n", i, e, g)
			continue
		}
	}
}

type headerOnlyResponseWriter Header

func (ho headerOnlyResponseWriter) Header() Header {
	return Header(ho)
}

func (ho headerOnlyResponseWriter) Write([]byte) (int, error) {
	panic("NOIMPL")
}

func (ho headerOnlyResponseWriter) WriteHeader(int) {
	panic("NOIMPL")
}

func TestSetCookie(t *testing.T) {
	m := make(Header)
	SetCookie(headerOnlyResponseWriter(m), &Cookie{Name: "cookie-1", Value: "one", Path: "/restricted/"})
	SetCookie(headerOnlyResponseWriter(m), &Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600})
	if l := len(m["Set-Cookie"]); l != 2 {
		t.Fatalf("expected %d cookies, got %d", 2, l)
	}
	if g, e := m["Set-Cookie"][0], "cookie-1=one; Path=/restricted/"; g != e {
		t.Errorf("cookie #1: want %q, got %q", e, g)
	}
	if g, e := m["Set-Cookie"][1], "cookie-2=two; Max-Age=3600"; g != e {
		t.Errorf("cookie #2: want %q, got %q", e, g)
	}
}

var addCookieTests = []struct {
	Cookies []*Cookie
	Raw     string
}{
	{
		[]*Cookie{},
		"",
	},
	{
		[]*Cookie{{Name: "cookie-1", Value: "v$1"}},
		"cookie-1=v$1",
	},
	{
		[]*Cookie{
			{Name: "cookie-1", Value: "v$1"},
			{Name: "cookie-2", Value: "v$2"},
			{Name: "cookie-3", Value: "v$3"},
		},
		"cookie-1=v$1; cookie-2=v$2; cookie-3=v$3",
	},
}

func TestAddCookie(t *testing.T) {
	for i, tt := range addCookieTests {
		req, _ := NewRequest("GET", "http://example.com/", nil)
		for _, c := range tt.Cookies {
			req.AddCookie(c)
		}
		if g := req.Header.Get("Cookie"); g != tt.Raw {
			t.Errorf("Test %d:\nwant: %s\n got: %s\n", i, tt.Raw, g)
			continue
		}
	}
}

var readSetCookiesTests = []struct {
	Header  Header
	Cookies []*Cookie
}{
	{
		Header{"Set-Cookie": {"Cookie-1=v$1"}},
		[]*Cookie{{Name: "Cookie-1", Value: "v$1", Raw: "Cookie-1=v$1"}},
	},
	{
		Header{"Set-Cookie": {"NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly"}},
		[]*Cookie{{
			Name:       "NID",
			Value:      "99=YsDT5i3E-CXax-",
			Path:       "/",
			Domain:     ".google.ch",
			HttpOnly:   true,
			Expires:    time.Date(2011, 11, 23, 1, 5, 3, 0, time.UTC),
			RawExpires: "Wed, 23-Nov-2011 01:05:03 GMT",
			Raw:        "NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly",
		}},
	},
}

func toJSON(v interface{}) string {
	b, err := json.Marshal(v)
	if err != nil {
		return fmt.Sprintf("%#v", v)
	}
	return string(b)
}

func TestReadSetCookies(t *testing.T) {
	for i, tt := range readSetCookiesTests {
		for n := 0; n < 2; n++ { // to verify readSetCookies doesn't mutate its input
			c := readSetCookies(tt.Header)
			if !reflect.DeepEqual(c, tt.Cookies) {
				t.Errorf("#%d readSetCookies: have\n%s\nwant\n%s\n", i, toJSON(c), toJSON(tt.Cookies))
				continue
			}
		}
	}
}

var readCookiesTests = []struct {
	Header  Header
	Filter  string
	Cookies []*Cookie
}{
	{
		Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}},
		"",
		[]*Cookie{
			{Name: "Cookie-1", Value: "v$1"},
			{Name: "c2", Value: "v2"},
		},
	},
	{
		Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}},
		"c2",
		[]*Cookie{
			{Name: "c2", Value: "v2"},
		},
	},
	{
		Header{"Cookie": {"Cookie-1=v$1; c2=v2"}},
		"",
		[]*Cookie{
			{Name: "Cookie-1", Value: "v$1"},
			{Name: "c2", Value: "v2"},
		},
	},
	{
		Header{"Cookie": {"Cookie-1=v$1; c2=v2"}},
		"c2",
		[]*Cookie{
			{Name: "c2", Value: "v2"},
		},
	},
}

func TestReadCookies(t *testing.T) {
	for i, tt := range readCookiesTests {
		for n := 0; n < 2; n++ { // to verify readCookies doesn't mutate its input
			c := readCookies(tt.Header, tt.Filter)
			if !reflect.DeepEqual(c, tt.Cookies) {
				t.Errorf("#%d readCookies:\nhave: %s\nwant: %s\n", i, toJSON(c), toJSON(tt.Cookies))
				continue
			}
		}
	}
}
<commit_msg>net/http: some more cookie tests<commit_after>// Copyright 2010 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package http

import (
	"encoding/json"
	"fmt"
	"reflect"
	"testing"
	"time"
)

var writeSetCookiesTests = []struct {
	Cookie *Cookie
	Raw    string
}{
	{
		&Cookie{Name: "cookie-1", Value: "v$1"},
		"cookie-1=v$1",
	},
	{
		&Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600},
		"cookie-2=two; Max-Age=3600",
	},
	{
		&Cookie{Name: "cookie-3", Value: "three", Domain: ".example.com"},
		"cookie-3=three; Domain=.example.com",
	},
	{
		&Cookie{Name: "cookie-4", Value: "four", Path: "/restricted/"},
		"cookie-4=four; Path=/restricted/",
	},
}

func TestWriteSetCookies(t *testing.T) {
	for i, tt := range writeSetCookiesTests {
		if g, e := tt.Cookie.String(), tt.Raw; g != e {
			t.Errorf("Test %d, expecting:\n%s\nGot:\n%s\n", i, e, g)
			continue
		}
	}
}

type headerOnlyResponseWriter Header

func (ho headerOnlyResponseWriter) Header() Header {
	return Header(ho)
}

func (ho headerOnlyResponseWriter) Write([]byte) (int, error) {
	panic("NOIMPL")
}

func (ho headerOnlyResponseWriter) WriteHeader(int) {
	panic("NOIMPL")
}

func TestSetCookie(t *testing.T) {
	m := make(Header)
	SetCookie(headerOnlyResponseWriter(m), &Cookie{Name: "cookie-1", Value: "one", Path: "/restricted/"})
	SetCookie(headerOnlyResponseWriter(m), &Cookie{Name: "cookie-2", Value: "two", MaxAge: 3600})
	if l := len(m["Set-Cookie"]); l != 2 {
		t.Fatalf("expected %d cookies, got %d", 2, l)
	}
	if g, e := m["Set-Cookie"][0], "cookie-1=one; Path=/restricted/"; g != e {
		t.Errorf("cookie #1: want %q, got %q", e, g)
	}
	if g, e := m["Set-Cookie"][1], "cookie-2=two; Max-Age=3600"; g != e {
		t.Errorf("cookie #2: want %q, got %q", e, g)
	}
}

var addCookieTests = []struct {
	Cookies []*Cookie
	Raw     string
}{
	{
		[]*Cookie{},
		"",
	},
	{
		[]*Cookie{{Name: "cookie-1", Value: "v$1"}},
		"cookie-1=v$1",
	},
	{
		[]*Cookie{
			{Name: "cookie-1", Value: "v$1"},
			{Name: "cookie-2", Value: "v$2"},
			{Name: "cookie-3", Value: "v$3"},
		},
		"cookie-1=v$1; cookie-2=v$2; cookie-3=v$3",
	},
}

func TestAddCookie(t *testing.T) {
	for i, tt := range addCookieTests {
		req, _ := NewRequest("GET", "http://example.com/", nil)
		for _, c := range tt.Cookies {
			req.AddCookie(c)
		}
		if g := req.Header.Get("Cookie"); g != tt.Raw {
			t.Errorf("Test %d:\nwant: %s\n got: %s\n", i, tt.Raw, g)
			continue
		}
	}
}

var readSetCookiesTests = []struct {
	Header  Header
	Cookies []*Cookie
}{
	{
		Header{"Set-Cookie": {"Cookie-1=v$1"}},
		[]*Cookie{{Name: "Cookie-1", Value: "v$1", Raw: "Cookie-1=v$1"}},
	},
	{
		Header{"Set-Cookie": {"NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly"}},
		[]*Cookie{{
			Name:       "NID",
			Value:      "99=YsDT5i3E-CXax-",
			Path:       "/",
			Domain:     ".google.ch",
			HttpOnly:   true,
			Expires:    time.Date(2011, 11, 23, 1, 5, 3, 0, time.UTC),
			RawExpires: "Wed, 23-Nov-2011 01:05:03 GMT",
			Raw:        "NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly",
		}},
	},
	{
		Header{"Set-Cookie": {".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly"}},
		[]*Cookie{{
			Name:       ".ASPXAUTH",
			Value:      "7E3AA",
			Path:       "/",
			Expires:    time.Date(2012, 3, 7, 14, 25, 6, 0, time.UTC),
			RawExpires: "Wed, 07-Mar-2012 14:25:06 GMT",
			HttpOnly:   true,
			Raw:        ".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly",
		}},
	},
	{
		Header{"Set-Cookie": {"ASP.NET_SessionId=foo; path=/; HttpOnly"}},
		[]*Cookie{{
			Name:     "ASP.NET_SessionId",
			Value:    "foo",
			Path:     "/",
			HttpOnly: true,
			Raw:      "ASP.NET_SessionId=foo; path=/; HttpOnly",
		}},
	},

	// TODO(bradfitz): users have reported seeing this in the
	// wild, but do browsers handle it? RFC 6265 just says "don't
	// do that" (section 3) and then never mentions header folding
	// again.
	// Header{"Set-Cookie": {"ASP.NET_SessionId=foo; path=/; HttpOnly, .ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly"}},
}

func toJSON(v interface{}) string {
	b, err := json.Marshal(v)
	if err != nil {
		return fmt.Sprintf("%#v", v)
	}
	return string(b)
}

func TestReadSetCookies(t *testing.T) {
	for i, tt := range readSetCookiesTests {
		for n := 0; n < 2; n++ { // to verify readSetCookies doesn't mutate its input
			c := readSetCookies(tt.Header)
			if !reflect.DeepEqual(c, tt.Cookies) {
				t.Errorf("#%d readSetCookies: have\n%s\nwant\n%s\n", i, toJSON(c), toJSON(tt.Cookies))
				continue
			}
		}
	}
}

var readCookiesTests = []struct {
	Header  Header
	Filter  string
	Cookies []*Cookie
}{
	{
		Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}},
		"",
		[]*Cookie{
			{Name: "Cookie-1", Value: "v$1"},
			{Name: "c2", Value: "v2"},
		},
	},
	{
		Header{"Cookie": {"Cookie-1=v$1", "c2=v2"}},
		"c2",
		[]*Cookie{
			{Name: "c2", Value: "v2"},
		},
	},
	{
		Header{"Cookie": {"Cookie-1=v$1; c2=v2"}},
		"",
		[]*Cookie{
			{Name: "Cookie-1", Value: "v$1"},
			{Name: "c2", Value: "v2"},
		},
	},
	{
		Header{"Cookie": {"Cookie-1=v$1; c2=v2"}},
		"c2",
		[]*Cookie{
			{Name: "c2", Value: "v2"},
		},
	},
}

func TestReadCookies(t *testing.T) {
	for i, tt := range readCookiesTests {
		for n := 0; n < 2; n++ { // to verify readCookies doesn't mutate its input
			c := readCookies(tt.Header, tt.Filter)
			if !reflect.DeepEqual(c, tt.Cookies) {
				t.Errorf("#%d readCookies:\nhave: %s\nwant: %s\n", i, toJSON(c), toJSON(tt.Cookies))
				continue
			}
		}
	}
}
<|endoftext|>


<commit_before>package timer_test

import (
	"fmt"
	"github.com/name5566/leaf/timer"
)

func Example() {
	d := timer.NewDispatcher(10)

	// timer 1
	d.AfterFunc(1, func() {
		fmt.Println("My name is Leaf")
	})

	// timer 2
	t := d.AfterFunc(1, func() {
		fmt.Println("will not print")
	})
	t.Stop()

	// dispatch
	(<-d.ChanTimer).Cb()

	// Output:
	// My name is Leaf
}
<commit_msg>example for CronExpr<commit_after>package timer_test

import (
	"fmt"
	"github.com/name5566/leaf/timer"
	"time"
)

func ExampleTimer() {
	d := timer.NewDispatcher(10)

	// timer 1
	d.AfterFunc(1, func() {
		fmt.Println("My name is Leaf")
	})

	// timer 2
	t := d.AfterFunc(1, func() {
		fmt.Println("will not print")
	})
	t.Stop()

	// dispatch
	(<-d.ChanTimer).Cb()

	// Output:
	// My name is Leaf
}

func ExampleCronExpr() {
	cronExpr, err := timer.NewCronExpr("0 * * * *")
	if err != nil {
		return
	}

	fmt.Println(cronExpr.Next(time.Date(
		2000, 1, 1,
		20, 10, 5,
		0, time.UTC,
	)))

	// Output:
	// 2000-01-01 21:00:00 +0000 UTC
}
<|endoftext|>


<commit_before>package client // import "github.com/docker/docker/client"

import (
	"bytes"
	"net/http"
	"net/url"
	"os"
	"runtime"
	"strings"
	"testing"

	"github.com/docker/docker/api"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/internal/testutil"
	"github.com/gotestyourself/gotestyourself/skip"
	"github.com/stretchr/testify/assert"
)

func TestNewEnvClient(t *testing.T) {
	skip.IfCondition(t, runtime.GOOS == "windows")

	testcases := []struct {
		doc             string
		envs            map[string]string
		expectedError   string
		expectedVersion string
	}{
		{
			doc:             "default api version",
			envs:            map[string]string{},
			expectedVersion: api.DefaultVersion,
		},
		{
			doc: "invalid cert path",
			envs: map[string]string{
				"DOCKER_CERT_PATH": "invalid/path",
			},
			expectedError: "Could not load X509 key pair: open invalid/path/cert.pem: no such file or directory",
		},
		{
			doc: "default api version with cert path",
			envs: map[string]string{
				"DOCKER_CERT_PATH": "testdata/",
			},
			expectedVersion: api.DefaultVersion,
		},
		{
			doc: "default api version with cert path and tls verify",
			envs: map[string]string{
				"DOCKER_CERT_PATH":  "testdata/",
				"DOCKER_TLS_VERIFY": "1",
			},
			expectedVersion: api.DefaultVersion,
		},
		{
			doc: "default api version with cert path and host",
			envs: map[string]string{
				"DOCKER_CERT_PATH": "testdata/",
				"DOCKER_HOST":      "https://notaunixsocket",
			},
			expectedVersion: api.DefaultVersion,
		},
		{
			doc: "invalid docker host",
			envs: map[string]string{
				"DOCKER_HOST": "host",
			},
			expectedError: "unable to parse docker host `host`",
		},
		{
			doc: "invalid docker host, with good format",
			envs: map[string]string{
				"DOCKER_HOST": "invalid://url",
			},
			expectedVersion: api.DefaultVersion,
		},
		{
			doc: "override api version",
			envs: map[string]string{
				"DOCKER_API_VERSION": "1.22",
			},
			expectedVersion: "1.22",
		},
	}

	env := envToMap()
	defer mapToEnv(env)
	for _, c := range testcases {
		mapToEnv(c.envs)
		apiclient, err := NewEnvClient()
		if c.expectedError != "" {
			assert.Error(t, err, c.doc)
			assert.Equal(t, c.expectedError, err.Error(), c.doc)
		} else {
			assert.NoError(t, err, c.doc)
			version := apiclient.ClientVersion()
			assert.Equal(t, c.expectedVersion, version, c.doc)
		}

		if c.envs["DOCKER_TLS_VERIFY"] != "" {
			// pedantic checking that this is handled correctly
			tr := apiclient.client.Transport.(*http.Transport)
			assert.NotNil(t, tr.TLSClientConfig, c.doc)
			assert.Equal(t, tr.TLSClientConfig.InsecureSkipVerify, false, c.doc)
		}
	}
}

func TestGetAPIPath(t *testing.T) {
	testcases := []struct {
		version  string
		path     string
		query    url.Values
		expected string
	}{
		{"", "/containers/json", nil, "/containers/json"},
		{"", "/containers/json", url.Values{}, "/containers/json"},
		{"", "/containers/json", url.Values{"s": []string{"c"}}, "/containers/json?s=c"},
		{"1.22", "/containers/json", nil, "/v1.22/containers/json"},
		{"1.22", "/containers/json", url.Values{}, "/v1.22/containers/json"},
		{"1.22", "/containers/json", url.Values{"s": []string{"c"}}, "/v1.22/containers/json?s=c"},
		{"v1.22", "/containers/json", nil, "/v1.22/containers/json"},
		{"v1.22", "/containers/json", url.Values{}, "/v1.22/containers/json"},
		{"v1.22", "/containers/json", url.Values{"s": []string{"c"}}, "/v1.22/containers/json?s=c"},
		{"v1.22", "/networks/kiwl$%^", nil, "/v1.22/networks/kiwl$%25%5E"},
	}

	for _, testcase := range testcases {
		c := Client{version: testcase.version, basePath: "/"}
		actual := c.getAPIPath(testcase.path, testcase.query)
		assert.Equal(t, actual, testcase.expected)
	}
}

func TestParseHost(t *testing.T) {
	cases := []struct {
		host  string
		proto string
		addr  string
		base  string
		err   bool
	}{
		{"", "", "", "", true},
		{"foobar", "", "", "", true},
		{"foo://bar", "foo", "bar", "", false},
		{"tcp://localhost:2476", "tcp", "localhost:2476", "", false},
		{"tcp://localhost:2476/path", "tcp", "localhost:2476", "/path", false},
	}

	for _, cs := range cases {
		p, a, b, e := ParseHost(cs.host)
		if cs.err {
			assert.Error(t, e)
		}
		assert.Equal(t, cs.proto, p)
		assert.Equal(t, cs.addr, a)
		assert.Equal(t, cs.base, b)
	}
}

func TestParseHostURL(t *testing.T) {
	testcases := []struct {
		host        string
		expected    *url.URL
		expectedErr string
	}{
		{
			host:        "",
			expectedErr: "unable to parse docker host",
		},
		{
			host:        "foobar",
			expectedErr: "unable to parse docker host",
		},
		{
			host:     "foo://bar",
			expected: &url.URL{Scheme: "foo", Host: "bar"},
		},
		{
			host:     "tcp://localhost:2476",
			expected: &url.URL{Scheme: "tcp", Host: "localhost:2476"},
		},
		{
			host:     "tcp://localhost:2476/path",
			expected: &url.URL{Scheme: "tcp", Host: "localhost:2476", Path: "/path"},
		},
	}

	for _, testcase := range testcases {
		actual, err := ParseHostURL(testcase.host)
		if testcase.expectedErr != "" {
			testutil.ErrorContains(t, err, testcase.expectedErr)
		}
		assert.Equal(t, testcase.expected, actual)
	}
}

func TestNewEnvClientSetsDefaultVersion(t *testing.T) {
	env := envToMap()
	defer mapToEnv(env)

	envMap := map[string]string{
		"DOCKER_HOST":        "",
		"DOCKER_API_VERSION": "",
		"DOCKER_TLS_VERIFY":  "",
		"DOCKER_CERT_PATH":   "",
	}
	mapToEnv(envMap)

	client, err := NewEnvClient()
	if err != nil {
		t.Fatal(err)
	}
	assert.Equal(t, client.version, api.DefaultVersion)

	expected := "1.22"
	os.Setenv("DOCKER_API_VERSION", expected)
	client, err = NewEnvClient()
	if err != nil {
		t.Fatal(err)
	}
	assert.Equal(t, expected, client.version)
}

// TestNegotiateAPIVersionEmpty asserts that client.Client can
// negotiate a compatible APIVersion when omitted
func TestNegotiateAPIVersionEmpty(t *testing.T) {
	env := envToMap()
	defer mapToEnv(env)

	envMap := map[string]string{
		"DOCKER_API_VERSION": "",
	}
	mapToEnv(envMap)

	client, err := NewEnvClient()
	if err != nil {
		t.Fatal(err)
	}

	ping := types.Ping{
		APIVersion:   "",
		OSType:       "linux",
		Experimental: false,
	}

	// set our version to something new
	client.version = "1.25"

	// if no version from server, expect the earliest
	// version before APIVersion was implemented
	expected := "1.24"

	// test downgrade
	client.NegotiateAPIVersionPing(ping)
	assert.Equal(t, expected, client.version)
}

// TestNegotiateAPIVersion asserts that client.Client can
// negotiate a compatible APIVersion with the server
func TestNegotiateAPIVersion(t *testing.T) {
	client, err := NewEnvClient()
	if err != nil {
		t.Fatal(err)
	}

	expected := "1.21"

	ping := types.Ping{
		APIVersion:   expected,
		OSType:       "linux",
		Experimental: false,
	}

	// set our version to something new
	client.version = "1.22"

	// test downgrade
	client.NegotiateAPIVersionPing(ping)
	assert.Equal(t, expected, client.version)

	// set the client version to something older, and verify that we keep the
	// original setting.
	expected = "1.20"
	client.version = expected
	client.NegotiateAPIVersionPing(ping)
	assert.Equal(t, expected, client.version)

}

// TestNegotiateAPIVersionOverride asserts that we honor
// the environment variable DOCKER_API_VERSION when negotianing versions
func TestNegotiateAPVersionOverride(t *testing.T) {
	env := envToMap()
	defer mapToEnv(env)

	envMap := map[string]string{
		"DOCKER_API_VERSION": "9.99",
	}
	mapToEnv(envMap)

	client, err := NewEnvClient()
	if err != nil {
		t.Fatal(err)
	}

	ping := types.Ping{
		APIVersion:   "1.24",
		OSType:       "linux",
		Experimental: false,
	}

	expected := envMap["DOCKER_API_VERSION"]

	// test that we honored the env var
	client.NegotiateAPIVersionPing(ping)
	assert.Equal(t, expected, client.version)
}

// mapToEnv takes a map of environment variables and sets them
func mapToEnv(env map[string]string) {
	for k, v := range env {
		os.Setenv(k, v)
	}
}

// envToMap returns a map of environment variables
func envToMap() map[string]string {
	env := make(map[string]string)
	for _, e := range os.Environ() {
		kv := strings.SplitAfterN(e, "=", 2)
		env[kv[0]] = kv[1]
	}

	return env
}

type roundTripFunc func(*http.Request) (*http.Response, error)

func (rtf roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
	return rtf(req)
}

type bytesBufferClose struct {
	*bytes.Buffer
}

func (bbc bytesBufferClose) Close() error {
	return nil
}

func TestClientRedirect(t *testing.T) {
	client := &http.Client{
		CheckRedirect: CheckRedirect,
		Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
			if req.URL.String() == "/bla" {
				return &http.Response{StatusCode: 404}, nil
			}
			return &http.Response{
				StatusCode: 301,
				Header:     map[string][]string{"Location": {"/bla"}},
				Body:       bytesBufferClose{bytes.NewBuffer(nil)},
			}, nil
		}),
	}

	cases := []struct {
		httpMethod  string
		expectedErr error
		statusCode  int
	}{
		{http.MethodGet, nil, 301},
		{http.MethodPost, &url.Error{Op: "Post", URL: "/bla", Err: ErrRedirect}, 301},
		{http.MethodPut, &url.Error{Op: "Put", URL: "/bla", Err: ErrRedirect}, 301},
		{http.MethodDelete, &url.Error{Op: "Delete", URL: "/bla", Err: ErrRedirect}, 301},
	}

	for _, tc := range cases {
		req, err := http.NewRequest(tc.httpMethod, "/redirectme", nil)
		assert.NoError(t, err)
		resp, err := client.Do(req)
		assert.Equal(t, tc.expectedErr, err)
		assert.Equal(t, tc.statusCode, resp.StatusCode)
	}
}
<commit_msg>Use gotestyourself env patching<commit_after>package client // import "github.com/docker/docker/client"

import (
	"bytes"
	"net/http"
	"net/url"
	"os"
	"runtime"
	"testing"

	"github.com/docker/docker/api"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/internal/testutil"
	"github.com/gotestyourself/gotestyourself/env"
	"github.com/gotestyourself/gotestyourself/skip"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestNewEnvClient(t *testing.T) {
	skip.If(t, runtime.GOOS == "windows")

	testcases := []struct {
		doc             string
		envs            map[string]string
		expectedError   string
		expectedVersion string
	}{
		{
			doc:             "default api version",
			envs:            map[string]string{},
			expectedVersion: api.DefaultVersion,
		},
		{
			doc: "invalid cert path",
			envs: map[string]string{
				"DOCKER_CERT_PATH": "invalid/path",
			},
			expectedError: "Could not load X509 key pair: open invalid/path/cert.pem: no such file or directory",
		},
		{
			doc: "default api version with cert path",
			envs: map[string]string{
				"DOCKER_CERT_PATH": "testdata/",
			},
			expectedVersion: api.DefaultVersion,
		},
		{
			doc: "default api version with cert path and tls verify",
			envs: map[string]string{
				"DOCKER_CERT_PATH":  "testdata/",
				"DOCKER_TLS_VERIFY": "1",
			},
			expectedVersion: api.DefaultVersion,
		},
		{
			doc: "default api version with cert path and host",
			envs: map[string]string{
				"DOCKER_CERT_PATH": "testdata/",
				"DOCKER_HOST":      "https://notaunixsocket",
			},
			expectedVersion: api.DefaultVersion,
		},
		{
			doc: "invalid docker host",
			envs: map[string]string{
				"DOCKER_HOST": "host",
			},
			expectedError: "unable to parse docker host `host`",
		},
		{
			doc: "invalid docker host, with good format",
			envs: map[string]string{
				"DOCKER_HOST": "invalid://url",
			},
			expectedVersion: api.DefaultVersion,
		},
		{
			doc: "override api version",
			envs: map[string]string{
				"DOCKER_API_VERSION": "1.22",
			},
			expectedVersion: "1.22",
		},
	}

	defer env.PatchAll(t, nil)()
	for _, c := range testcases {
		env.PatchAll(t, c.envs)
		apiclient, err := NewEnvClient()
		if c.expectedError != "" {
			assert.Error(t, err, c.doc)
			assert.Equal(t, c.expectedError, err.Error(), c.doc)
		} else {
			assert.NoError(t, err, c.doc)
			version := apiclient.ClientVersion()
			assert.Equal(t, c.expectedVersion, version, c.doc)
		}

		if c.envs["DOCKER_TLS_VERIFY"] != "" {
			// pedantic checking that this is handled correctly
			tr := apiclient.client.Transport.(*http.Transport)
			assert.NotNil(t, tr.TLSClientConfig, c.doc)
			assert.Equal(t, tr.TLSClientConfig.InsecureSkipVerify, false, c.doc)
		}
	}
}

func TestGetAPIPath(t *testing.T) {
	testcases := []struct {
		version  string
		path     string
		query    url.Values
		expected string
	}{
		{"", "/containers/json", nil, "/containers/json"},
		{"", "/containers/json", url.Values{}, "/containers/json"},
		{"", "/containers/json", url.Values{"s": []string{"c"}}, "/containers/json?s=c"},
		{"1.22", "/containers/json", nil, "/v1.22/containers/json"},
		{"1.22", "/containers/json", url.Values{}, "/v1.22/containers/json"},
		{"1.22", "/containers/json", url.Values{"s": []string{"c"}}, "/v1.22/containers/json?s=c"},
		{"v1.22", "/containers/json", nil, "/v1.22/containers/json"},
		{"v1.22", "/containers/json", url.Values{}, "/v1.22/containers/json"},
		{"v1.22", "/containers/json", url.Values{"s": []string{"c"}}, "/v1.22/containers/json?s=c"},
		{"v1.22", "/networks/kiwl$%^", nil, "/v1.22/networks/kiwl$%25%5E"},
	}

	for _, testcase := range testcases {
		c := Client{version: testcase.version, basePath: "/"}
		actual := c.getAPIPath(testcase.path, testcase.query)
		assert.Equal(t, actual, testcase.expected)
	}
}

func TestParseHost(t *testing.T) {
	cases := []struct {
		host  string
		proto string
		addr  string
		base  string
		err   bool
	}{
		{"", "", "", "", true},
		{"foobar", "", "", "", true},
		{"foo://bar", "foo", "bar", "", false},
		{"tcp://localhost:2476", "tcp", "localhost:2476", "", false},
		{"tcp://localhost:2476/path", "tcp", "localhost:2476", "/path", false},
	}

	for _, cs := range cases {
		p, a, b, e := ParseHost(cs.host)
		if cs.err {
			assert.Error(t, e)
		}
		assert.Equal(t, cs.proto, p)
		assert.Equal(t, cs.addr, a)
		assert.Equal(t, cs.base, b)
	}
}

func TestParseHostURL(t *testing.T) {
	testcases := []struct {
		host        string
		expected    *url.URL
		expectedErr string
	}{
		{
			host:        "",
			expectedErr: "unable to parse docker host",
		},
		{
			host:        "foobar",
			expectedErr: "unable to parse docker host",
		},
		{
			host:     "foo://bar",
			expected: &url.URL{Scheme: "foo", Host: "bar"},
		},
		{
			host:     "tcp://localhost:2476",
			expected: &url.URL{Scheme: "tcp", Host: "localhost:2476"},
		},
		{
			host:     "tcp://localhost:2476/path",
			expected: &url.URL{Scheme: "tcp", Host: "localhost:2476", Path: "/path"},
		},
	}

	for _, testcase := range testcases {
		actual, err := ParseHostURL(testcase.host)
		if testcase.expectedErr != "" {
			testutil.ErrorContains(t, err, testcase.expectedErr)
		}
		assert.Equal(t, testcase.expected, actual)
	}
}

func TestNewEnvClientSetsDefaultVersion(t *testing.T) {
	defer env.PatchAll(t, map[string]string{
		"DOCKER_HOST":        "",
		"DOCKER_API_VERSION": "",
		"DOCKER_TLS_VERIFY":  "",
		"DOCKER_CERT_PATH":   "",
	})()

	client, err := NewEnvClient()
	if err != nil {
		t.Fatal(err)
	}
	assert.Equal(t, client.version, api.DefaultVersion)

	expected := "1.22"
	os.Setenv("DOCKER_API_VERSION", expected)
	client, err = NewEnvClient()
	if err != nil {
		t.Fatal(err)
	}
	assert.Equal(t, expected, client.version)
}

// TestNegotiateAPIVersionEmpty asserts that client.Client can
// negotiate a compatible APIVersion when omitted
func TestNegotiateAPIVersionEmpty(t *testing.T) {
	defer env.PatchAll(t, map[string]string{"DOCKER_API_VERSION": ""})

	client, err := NewEnvClient()
	require.NoError(t, err)

	ping := types.Ping{
		APIVersion:   "",
		OSType:       "linux",
		Experimental: false,
	}

	// set our version to something new
	client.version = "1.25"

	// if no version from server, expect the earliest
	// version before APIVersion was implemented
	expected := "1.24"

	// test downgrade
	client.NegotiateAPIVersionPing(ping)
	assert.Equal(t, expected, client.version)
}

// TestNegotiateAPIVersion asserts that client.Client can
// negotiate a compatible APIVersion with the server
func TestNegotiateAPIVersion(t *testing.T) {
	client, err := NewEnvClient()
	require.NoError(t, err)

	expected := "1.21"
	ping := types.Ping{
		APIVersion:   expected,
		OSType:       "linux",
		Experimental: false,
	}

	// set our version to something new
	client.version = "1.22"

	// test downgrade
	client.NegotiateAPIVersionPing(ping)
	assert.Equal(t, expected, client.version)

	// set the client version to something older, and verify that we keep the
	// original setting.
	expected = "1.20"
	client.version = expected
	client.NegotiateAPIVersionPing(ping)
	assert.Equal(t, expected, client.version)

}

// TestNegotiateAPIVersionOverride asserts that we honor
// the environment variable DOCKER_API_VERSION when negotianing versions
func TestNegotiateAPVersionOverride(t *testing.T) {
	expected := "9.99"
	defer env.PatchAll(t, map[string]string{"DOCKER_API_VERSION": expected})()

	client, err := NewEnvClient()
	require.NoError(t, err)

	ping := types.Ping{
		APIVersion:   "1.24",
		OSType:       "linux",
		Experimental: false,
	}

	// test that we honored the env var
	client.NegotiateAPIVersionPing(ping)
	assert.Equal(t, expected, client.version)
}

type roundTripFunc func(*http.Request) (*http.Response, error)

func (rtf roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
	return rtf(req)
}

type bytesBufferClose struct {
	*bytes.Buffer
}

func (bbc bytesBufferClose) Close() error {
	return nil
}

func TestClientRedirect(t *testing.T) {
	client := &http.Client{
		CheckRedirect: CheckRedirect,
		Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
			if req.URL.String() == "/bla" {
				return &http.Response{StatusCode: 404}, nil
			}
			return &http.Response{
				StatusCode: 301,
				Header:     map[string][]string{"Location": {"/bla"}},
				Body:       bytesBufferClose{bytes.NewBuffer(nil)},
			}, nil
		}),
	}

	cases := []struct {
		httpMethod  string
		expectedErr error
		statusCode  int
	}{
		{http.MethodGet, nil, 301},
		{http.MethodPost, &url.Error{Op: "Post", URL: "/bla", Err: ErrRedirect}, 301},
		{http.MethodPut, &url.Error{Op: "Put", URL: "/bla", Err: ErrRedirect}, 301},
		{http.MethodDelete, &url.Error{Op: "Delete", URL: "/bla", Err: ErrRedirect}, 301},
	}

	for _, tc := range cases {
		req, err := http.NewRequest(tc.httpMethod, "/redirectme", nil)
		assert.NoError(t, err)
		resp, err := client.Do(req)
		assert.Equal(t, tc.expectedErr, err)
		assert.Equal(t, tc.statusCode, resp.StatusCode)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Knative 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 v1

import (
	"fmt"

	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"

	"knative.dev/pkg/apis"
	av1alpha1 "knative.dev/serving/pkg/apis/autoscaling/v1alpha1"
	"knative.dev/serving/pkg/apis/config"
	"knative.dev/serving/pkg/apis/serving"
)

const (
	// ReasonContainerMissing defines the reason for marking container healthiness status
	// as false if the a container image for the revision is missing.
	ReasonContainerMissing = "ContainerMissing"

	// ReasonResolvingDigests defines the reason for marking container healthiness status
	// as unknown if the digests for the container images are being resolved.
	ReasonResolvingDigests = "ResolvingDigests"

	// ReasonDeploying defines the reason for marking revision availability status as
	// unknown if the revision is still deploying.
	ReasonDeploying = "Deploying"

	// ReasonNotOwned defines the reason for marking revision availability status as
	// false due to resource ownership issues.
	ReasonNotOwned = "NotOwned"

	// ReasonProgressDeadlineExceeded defines the reason for marking revision availability
	// status as false if progress has exceeded the deadline.
	ReasonProgressDeadlineExceeded = "ProgressDeadlineExceeded"
)

var revisionCondSet = apis.NewLivingConditionSet(
	RevisionConditionResourcesAvailable,
	RevisionConditionContainerHealthy,
)

// GetConditionSet retrieves the condition set for this resource. Implements the KRShaped interface.
func (*Revision) GetConditionSet() apis.ConditionSet {
	return revisionCondSet
}

// GetGroupVersionKind returns the GroupVersionKind.
func (r *Revision) GetGroupVersionKind() schema.GroupVersionKind {
	return SchemeGroupVersion.WithKind("Revision")
}

// IsReady returns true if the Status condition RevisionConditionReady
// is true and the latest spec has been observed.
func (r *Revision) IsReady() bool {
	rs := r.Status
	return rs.ObservedGeneration == r.Generation &&
		rs.GetCondition(RevisionConditionReady).IsTrue()
}

// IsFailed returns true if the resource has observed the latest generation
// and ready is false.
func (r *Revision) IsFailed() bool {
	rs := r.Status
	return rs.ObservedGeneration == r.Generation &&
		rs.GetCondition(RevisionConditionReady).IsFalse()
}

// GetContainerConcurrency returns the container concurrency. If
// container concurrency is not set, the default value will be returned.
// We use the original default (0) here for backwards compatibility.
// Previous versions of Knative equated unspecified and zero, so to avoid
// changing the value used by Revisions with unspecified values when a different
// default is configured, we use the original default instead of the configured
// default to remain safe across upgrades.
func (rs *RevisionSpec) GetContainerConcurrency() int64 {
	if rs.ContainerConcurrency == nil {
		return config.DefaultContainerConcurrency
	}
	return *rs.ContainerConcurrency
}

// InitializeConditions sets the initial values to the conditions.
func (rs *RevisionStatus) InitializeConditions() {
	revisionCondSet.Manage(rs).InitializeConditions()
}

// MarkActiveTrue marks Active status on revision as True
func (rs *RevisionStatus) MarkActiveTrue() {
	revisionCondSet.Manage(rs).MarkTrue(RevisionConditionActive)
}

// MarkActiveFalse marks Active status on revision as False
func (rs *RevisionStatus) MarkActiveFalse(reason, message string) {
	revisionCondSet.Manage(rs).MarkFalse(RevisionConditionActive, reason, message)
}

// MarkActiveUnknown marks Active status on revision as Unknown
func (rs *RevisionStatus) MarkActiveUnknown(reason, message string) {
	revisionCondSet.Manage(rs).MarkUnknown(RevisionConditionActive, reason, message)
}

// MarkContainerHealthyTrue marks ContainerHealthy status on revision as True
func (rs *RevisionStatus) MarkContainerHealthyTrue() {
	revisionCondSet.Manage(rs).MarkTrue(RevisionConditionContainerHealthy)
}

// MarkContainerHealthyFalse marks ContainerHealthy status on revision as False
func (rs *RevisionStatus) MarkContainerHealthyFalse(reason, message string) {
	revisionCondSet.Manage(rs).MarkFalse(RevisionConditionContainerHealthy, reason, message)
}

// MarkContainerHealthyUnknown marks ContainerHealthy status on revision as Unknown
func (rs *RevisionStatus) MarkContainerHealthyUnknown(reason, message string) {
	revisionCondSet.Manage(rs).MarkUnknown(RevisionConditionContainerHealthy, reason, message)
}

// MarkResourcesAvailableTrue marks ResourcesAvailable status on revision as True
func (rs *RevisionStatus) MarkResourcesAvailableTrue() {
	revisionCondSet.Manage(rs).MarkTrue(RevisionConditionResourcesAvailable)
}

// MarkResourcesAvailableFalse marks ResourcesAvailable status on revision as False
func (rs *RevisionStatus) MarkResourcesAvailableFalse(reason, message string) {
	revisionCondSet.Manage(rs).MarkFalse(RevisionConditionResourcesAvailable, reason, message)
}

// MarkResourcesAvailableUnknown marks ResourcesAvailable status on revision as Unknown
func (rs *RevisionStatus) MarkResourcesAvailableUnknown(reason, message string) {
	revisionCondSet.Manage(rs).MarkUnknown(RevisionConditionResourcesAvailable, reason, message)
}

// PropagateDeploymentStatus takes the Deployment status and applies its values
// to the Revision status.
func (rs *RevisionStatus) PropagateDeploymentStatus(original *appsv1.DeploymentStatus) {
	ds := serving.TransformDeploymentStatus(original)
	cond := ds.GetCondition(serving.DeploymentConditionReady)

	m := revisionCondSet.Manage(rs)
	switch cond.Status {
	case corev1.ConditionTrue:
		m.MarkTrue(RevisionConditionResourcesAvailable)
	case corev1.ConditionFalse:
		m.MarkFalse(RevisionConditionResourcesAvailable, cond.Reason, cond.Message)
	case corev1.ConditionUnknown:
		m.MarkUnknown(RevisionConditionResourcesAvailable, cond.Reason, cond.Message)
	}
}

// PropagateAutoscalerStatus propagates autoscaler's status to the revision's status.
func (rs *RevisionStatus) PropagateAutoscalerStatus(ps *av1alpha1.PodAutoscalerStatus) {
	// Propagate the service name from the PA.
	rs.ServiceName = ps.ServiceName

	// Reflect the PA status in our own.
	cond := ps.GetCondition(av1alpha1.PodAutoscalerConditionReady)
	if cond == nil {
		rs.MarkActiveUnknown("Deploying", "")
		return
	}

	// Don't mark the resources available, if deployment status already determined
	// it isn't so.
	resUnavailable := !rs.GetCondition(RevisionConditionResourcesAvailable).IsFalse()
	if ps.IsScaleTargetInitialized() && resUnavailable {
		// Precondition for PA being initialized is SKS being active and
		// that implies that |service.endpoints| > 0.
		rs.MarkResourcesAvailableTrue()
		rs.MarkContainerHealthyTrue()
	}

	switch cond.Status {
	case corev1.ConditionUnknown:
		rs.MarkActiveUnknown(cond.Reason, cond.Message)
	case corev1.ConditionFalse:
		// Here we have 2 things coming together at the same time:
		// 1. The ready is False, meaning the revision is scaled to 0
		// 2. Initial scale was never achieved, which means we failed to progress
		//    towards initial scale during the progress deadline period and scaled to 0
		//		failing to activate.
		// So mark the revision as failed at that point.
		// See #8922 for details. When we try to scale to 0, we force the Deployment's
		// Progress status to become `true`, since successful scale down means
		// progress has been achieved.
		// There's the possibility of the revision reconciler reconciling PA before
		// the ServiceName is populated, and therefore even though we will mark
		// ScaleTargetInitialized down the road, we would have marked resources
		// unavailable here, and have no way of recovering later.
		// If the ResourcesAvailable is already false, don't override the message.
		if !ps.IsScaleTargetInitialized() && resUnavailable && ps.ServiceName != "" {
			rs.MarkResourcesAvailableFalse(ReasonProgressDeadlineExceeded,
				"Initial scale was never achieved")
		}
		rs.MarkActiveFalse(cond.Reason, cond.Message)
	case corev1.ConditionTrue:
		rs.MarkActiveTrue()

		// Precondition for PA being active is SKS being active and
		// that implies that |service.endpoints| > 0.
		//
		// Note: This is needed for backwards compatibility as we're adding the new
		// ScaleTargetInitialized condition to gate readiness.
		rs.MarkResourcesAvailableTrue()
		rs.MarkContainerHealthyTrue()
	}
}

// ResourceNotOwnedMessage constructs the status message if ownership on the
// resource is not right.
func ResourceNotOwnedMessage(kind, name string) string {
	return fmt.Sprintf("There is an existing %s %q that we do not own.", kind, name)
}

// ExitCodeReason constructs the status message from an exit code
func ExitCodeReason(exitCode int32) string {
	return fmt.Sprint("ExitCode", exitCode)
}

// RevisionContainerExitingMessage constructs the status message if a container
// fails to come up.
func RevisionContainerExitingMessage(message string) string {
	return fmt.Sprint("Container failed with: ", message)
}

// RevisionContainerMissingMessage constructs the status message if a given image
// cannot be pulled correctly.
func RevisionContainerMissingMessage(image string, message string) string {
	return fmt.Sprintf("Unable to fetch image %q: %s", image, message)
}
<commit_msg>Move negation of resource available condition (#9798)<commit_after>/*
Copyright 2019 The Knative 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 v1

import (
	"fmt"

	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"

	"knative.dev/pkg/apis"
	av1alpha1 "knative.dev/serving/pkg/apis/autoscaling/v1alpha1"
	"knative.dev/serving/pkg/apis/config"
	"knative.dev/serving/pkg/apis/serving"
)

const (
	// ReasonContainerMissing defines the reason for marking container healthiness status
	// as false if the a container image for the revision is missing.
	ReasonContainerMissing = "ContainerMissing"

	// ReasonResolvingDigests defines the reason for marking container healthiness status
	// as unknown if the digests for the container images are being resolved.
	ReasonResolvingDigests = "ResolvingDigests"

	// ReasonDeploying defines the reason for marking revision availability status as
	// unknown if the revision is still deploying.
	ReasonDeploying = "Deploying"

	// ReasonNotOwned defines the reason for marking revision availability status as
	// false due to resource ownership issues.
	ReasonNotOwned = "NotOwned"

	// ReasonProgressDeadlineExceeded defines the reason for marking revision availability
	// status as false if progress has exceeded the deadline.
	ReasonProgressDeadlineExceeded = "ProgressDeadlineExceeded"
)

var revisionCondSet = apis.NewLivingConditionSet(
	RevisionConditionResourcesAvailable,
	RevisionConditionContainerHealthy,
)

// GetConditionSet retrieves the condition set for this resource. Implements the KRShaped interface.
func (*Revision) GetConditionSet() apis.ConditionSet {
	return revisionCondSet
}

// GetGroupVersionKind returns the GroupVersionKind.
func (r *Revision) GetGroupVersionKind() schema.GroupVersionKind {
	return SchemeGroupVersion.WithKind("Revision")
}

// IsReady returns true if the Status condition RevisionConditionReady
// is true and the latest spec has been observed.
func (r *Revision) IsReady() bool {
	rs := r.Status
	return rs.ObservedGeneration == r.Generation &&
		rs.GetCondition(RevisionConditionReady).IsTrue()
}

// IsFailed returns true if the resource has observed the latest generation
// and ready is false.
func (r *Revision) IsFailed() bool {
	rs := r.Status
	return rs.ObservedGeneration == r.Generation &&
		rs.GetCondition(RevisionConditionReady).IsFalse()
}

// GetContainerConcurrency returns the container concurrency. If
// container concurrency is not set, the default value will be returned.
// We use the original default (0) here for backwards compatibility.
// Previous versions of Knative equated unspecified and zero, so to avoid
// changing the value used by Revisions with unspecified values when a different
// default is configured, we use the original default instead of the configured
// default to remain safe across upgrades.
func (rs *RevisionSpec) GetContainerConcurrency() int64 {
	if rs.ContainerConcurrency == nil {
		return config.DefaultContainerConcurrency
	}
	return *rs.ContainerConcurrency
}

// InitializeConditions sets the initial values to the conditions.
func (rs *RevisionStatus) InitializeConditions() {
	revisionCondSet.Manage(rs).InitializeConditions()
}

// MarkActiveTrue marks Active status on revision as True
func (rs *RevisionStatus) MarkActiveTrue() {
	revisionCondSet.Manage(rs).MarkTrue(RevisionConditionActive)
}

// MarkActiveFalse marks Active status on revision as False
func (rs *RevisionStatus) MarkActiveFalse(reason, message string) {
	revisionCondSet.Manage(rs).MarkFalse(RevisionConditionActive, reason, message)
}

// MarkActiveUnknown marks Active status on revision as Unknown
func (rs *RevisionStatus) MarkActiveUnknown(reason, message string) {
	revisionCondSet.Manage(rs).MarkUnknown(RevisionConditionActive, reason, message)
}

// MarkContainerHealthyTrue marks ContainerHealthy status on revision as True
func (rs *RevisionStatus) MarkContainerHealthyTrue() {
	revisionCondSet.Manage(rs).MarkTrue(RevisionConditionContainerHealthy)
}

// MarkContainerHealthyFalse marks ContainerHealthy status on revision as False
func (rs *RevisionStatus) MarkContainerHealthyFalse(reason, message string) {
	revisionCondSet.Manage(rs).MarkFalse(RevisionConditionContainerHealthy, reason, message)
}

// MarkContainerHealthyUnknown marks ContainerHealthy status on revision as Unknown
func (rs *RevisionStatus) MarkContainerHealthyUnknown(reason, message string) {
	revisionCondSet.Manage(rs).MarkUnknown(RevisionConditionContainerHealthy, reason, message)
}

// MarkResourcesAvailableTrue marks ResourcesAvailable status on revision as True
func (rs *RevisionStatus) MarkResourcesAvailableTrue() {
	revisionCondSet.Manage(rs).MarkTrue(RevisionConditionResourcesAvailable)
}

// MarkResourcesAvailableFalse marks ResourcesAvailable status on revision as False
func (rs *RevisionStatus) MarkResourcesAvailableFalse(reason, message string) {
	revisionCondSet.Manage(rs).MarkFalse(RevisionConditionResourcesAvailable, reason, message)
}

// MarkResourcesAvailableUnknown marks ResourcesAvailable status on revision as Unknown
func (rs *RevisionStatus) MarkResourcesAvailableUnknown(reason, message string) {
	revisionCondSet.Manage(rs).MarkUnknown(RevisionConditionResourcesAvailable, reason, message)
}

// PropagateDeploymentStatus takes the Deployment status and applies its values
// to the Revision status.
func (rs *RevisionStatus) PropagateDeploymentStatus(original *appsv1.DeploymentStatus) {
	ds := serving.TransformDeploymentStatus(original)
	cond := ds.GetCondition(serving.DeploymentConditionReady)

	m := revisionCondSet.Manage(rs)
	switch cond.Status {
	case corev1.ConditionTrue:
		m.MarkTrue(RevisionConditionResourcesAvailable)
	case corev1.ConditionFalse:
		m.MarkFalse(RevisionConditionResourcesAvailable, cond.Reason, cond.Message)
	case corev1.ConditionUnknown:
		m.MarkUnknown(RevisionConditionResourcesAvailable, cond.Reason, cond.Message)
	}
}

// PropagateAutoscalerStatus propagates autoscaler's status to the revision's status.
func (rs *RevisionStatus) PropagateAutoscalerStatus(ps *av1alpha1.PodAutoscalerStatus) {
	// Propagate the service name from the PA.
	rs.ServiceName = ps.ServiceName

	// Reflect the PA status in our own.
	cond := ps.GetCondition(av1alpha1.PodAutoscalerConditionReady)
	if cond == nil {
		rs.MarkActiveUnknown("Deploying", "")
		return
	}

	// Don't mark the resources available, if deployment status already determined
	// it isn't so.
	resUnavailable := rs.GetCondition(RevisionConditionResourcesAvailable).IsFalse()
	if ps.IsScaleTargetInitialized() && !resUnavailable {
		// Precondition for PA being initialized is SKS being active and
		// that implies that |service.endpoints| > 0.
		rs.MarkResourcesAvailableTrue()
		rs.MarkContainerHealthyTrue()
	}

	switch cond.Status {
	case corev1.ConditionUnknown:
		rs.MarkActiveUnknown(cond.Reason, cond.Message)
	case corev1.ConditionFalse:
		// Here we have 2 things coming together at the same time:
		// 1. The ready is False, meaning the revision is scaled to 0
		// 2. Initial scale was never achieved, which means we failed to progress
		//    towards initial scale during the progress deadline period and scaled to 0
		//		failing to activate.
		// So mark the revision as failed at that point.
		// See #8922 for details. When we try to scale to 0, we force the Deployment's
		// Progress status to become `true`, since successful scale down means
		// progress has been achieved.
		// There's the possibility of the revision reconciler reconciling PA before
		// the ServiceName is populated, and therefore even though we will mark
		// ScaleTargetInitialized down the road, we would have marked resources
		// unavailable here, and have no way of recovering later.
		// If the ResourcesAvailable is already false, don't override the message.
		if !ps.IsScaleTargetInitialized() && !resUnavailable && ps.ServiceName != "" {
			rs.MarkResourcesAvailableFalse(ReasonProgressDeadlineExceeded,
				"Initial scale was never achieved")
		}
		rs.MarkActiveFalse(cond.Reason, cond.Message)
	case corev1.ConditionTrue:
		rs.MarkActiveTrue()

		// Precondition for PA being active is SKS being active and
		// that implies that |service.endpoints| > 0.
		//
		// Note: This is needed for backwards compatibility as we're adding the new
		// ScaleTargetInitialized condition to gate readiness.
		rs.MarkResourcesAvailableTrue()
		rs.MarkContainerHealthyTrue()
	}
}

// ResourceNotOwnedMessage constructs the status message if ownership on the
// resource is not right.
func ResourceNotOwnedMessage(kind, name string) string {
	return fmt.Sprintf("There is an existing %s %q that we do not own.", kind, name)
}

// ExitCodeReason constructs the status message from an exit code
func ExitCodeReason(exitCode int32) string {
	return fmt.Sprint("ExitCode", exitCode)
}

// RevisionContainerExitingMessage constructs the status message if a container
// fails to come up.
func RevisionContainerExitingMessage(message string) string {
	return fmt.Sprint("Container failed with: ", message)
}

// RevisionContainerMissingMessage constructs the status message if a given image
// cannot be pulled correctly.
func RevisionContainerMissingMessage(image string, message string) string {
	return fmt.Sprintf("Unable to fetch image %q: %s", image, message)
}
<|endoftext|>


<commit_before>package resource

import (
	"testing"

	"reflect"

	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/kubectl/pkg/loader"
)

var encoded = []byte(`apiVersion: v1
kind: Deployment
metadata:
  name: dply1
---
apiVersion: v1
kind: Deployment
metadata:
  name: dply2
`)

type fakeLoader struct {
}

func (l fakeLoader) New(newRoot string) (loader.Loader, error) {
	return l, nil
}

func (l fakeLoader) Load(location string) ([]byte, error) {
	return encoded, nil
}

func makeUnconstructed(name string) *unstructured.Unstructured {
	return &unstructured.Unstructured{
		Object: map[string]interface{}{
			"apiVersion": "v1",
			"kind":       "Deployment",
			"metadata": map[string]interface{}{
				"name": name,
			},
		},
	}
}

func TestAppResourceList_Resources(t *testing.T) {
	l := fakeLoader{}
	expected := []*Resource{
		{Data: makeUnconstructed("dply1")},
		{Data: makeUnconstructed("dply2")},
	}

	resources, _ := ResourcesFromPath("fake/path", l)
	if len(resources) != 2 {
		t.Fatalf("%#v should contain 2 appResource, but got %d", resources, len(resources))
	}

	for i, r := range resources {
		if !reflect.DeepEqual(r.Data, expected[i].Data) {
			t.Fatalf("expected %v, but got %v", expected[i].Data, r.Data)
		}
	}
}
<commit_msg>Fix change to Loader interface; Root() method added to fake loader<commit_after>package resource

import (
	"testing"

	"reflect"

	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/kubectl/pkg/loader"
)

var encoded = []byte(`apiVersion: v1
kind: Deployment
metadata:
  name: dply1
---
apiVersion: v1
kind: Deployment
metadata:
  name: dply2
`)

type fakeLoader struct {
}

func (l fakeLoader) Root() string {
	return "unused"
}

func (l fakeLoader) New(newRoot string) (loader.Loader, error) {
	return l, nil
}

func (l fakeLoader) Load(location string) ([]byte, error) {
	return encoded, nil
}

func makeUnconstructed(name string) *unstructured.Unstructured {
	return &unstructured.Unstructured{
		Object: map[string]interface{}{
			"apiVersion": "v1",
			"kind":       "Deployment",
			"metadata": map[string]interface{}{
				"name": name,
			},
		},
	}
}

func TestAppResourceList_Resources(t *testing.T) {
	l := fakeLoader{}
	expected := []*Resource{
		{Data: makeUnconstructed("dply1")},
		{Data: makeUnconstructed("dply2")},
	}

	resources, _ := ResourcesFromPath("fake/path", l)
	if len(resources) != 2 {
		t.Fatalf("%#v should contain 2 appResource, but got %d", resources, len(resources))
	}

	for i, r := range resources {
		if !reflect.DeepEqual(r.Data, expected[i].Data) {
			t.Fatalf("expected %v, but got %v", expected[i].Data, r.Data)
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2017 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 server

import (
	"net"
	"net/http"
	"time"

	"github.com/golang/glog"

	"k8s.io/client-go/rest"
)

// DeprecatedInsecureServingInfo is the main context object for the insecure http server.
type DeprecatedInsecureServingInfo struct {
	// Listener is the secure server network listener.
	Listener net.Listener
	// optional server name for log messages
	Name string
}

// Serve starts an insecure http server with the given handler. It fails only if
// the initial listen call fails. It does not block.
func (s *DeprecatedInsecureServingInfo) Serve(handler http.Handler, shutdownTimeout time.Duration, stopCh <-chan struct{}) error {
	insecureServer := &http.Server{
		Addr:           s.Listener.Addr().String(),
		Handler:        handler,
		MaxHeaderBytes: 1 << 20,
	}

	if len(s.Name) > 0 {
		glog.Infof("Serving %s insecurely on %s", s.Name, s.Listener.Addr())
	} else {
		glog.Infof("Serving insecurely on %s", s.Listener.Addr())
	}
	return RunServer(insecureServer, s.Listener, shutdownTimeout, stopCh)
}

func (s *DeprecatedInsecureServingInfo) NewLoopbackClientConfig() (*rest.Config, error) {
	if s == nil {
		return nil, nil
	}

	host, port, err := LoopbackHostPort(s.Listener.Addr().String())
	if err != nil {
		return nil, err
	}

	return &rest.Config{
		Host: "http://" + net.JoinHostPort(host, port),
		// Increase QPS limits. The client is currently passed to all admission plugins,
		// and those can be throttled in case of higher load on apiserver - see #22340 and #22422
		// for more details. Once #22422 is fixed, we may want to remove it.
		QPS:   50,
		Burst: 100,
	}, nil
}
<commit_msg>kube-controller-manager: disable authn/z on insecure port<commit_after>/*
Copyright 2017 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 server

import (
	"net"
	"net/http"
	"time"

	"github.com/golang/glog"

	"k8s.io/apiserver/pkg/authentication/user"
	"k8s.io/client-go/rest"
)

// DeprecatedInsecureServingInfo is the main context object for the insecure http server.
type DeprecatedInsecureServingInfo struct {
	// Listener is the secure server network listener.
	Listener net.Listener
	// optional server name for log messages
	Name string
}

// Serve starts an insecure http server with the given handler. It fails only if
// the initial listen call fails. It does not block.
func (s *DeprecatedInsecureServingInfo) Serve(handler http.Handler, shutdownTimeout time.Duration, stopCh <-chan struct{}) error {
	insecureServer := &http.Server{
		Addr:           s.Listener.Addr().String(),
		Handler:        handler,
		MaxHeaderBytes: 1 << 20,
	}

	if len(s.Name) > 0 {
		glog.Infof("Serving %s insecurely on %s", s.Name, s.Listener.Addr())
	} else {
		glog.Infof("Serving insecurely on %s", s.Listener.Addr())
	}
	return RunServer(insecureServer, s.Listener, shutdownTimeout, stopCh)
}

func (s *DeprecatedInsecureServingInfo) NewLoopbackClientConfig() (*rest.Config, error) {
	if s == nil {
		return nil, nil
	}

	host, port, err := LoopbackHostPort(s.Listener.Addr().String())
	if err != nil {
		return nil, err
	}

	return &rest.Config{
		Host: "http://" + net.JoinHostPort(host, port),
		// Increase QPS limits. The client is currently passed to all admission plugins,
		// and those can be throttled in case of higher load on apiserver - see #22340 and #22422
		// for more details. Once #22422 is fixed, we may want to remove it.
		QPS:   50,
		Burst: 100,
	}, nil
}

// InsecureSuperuser implements authenticator.Request to always return a superuser.
// This is functionally equivalent to skipping authentication and authorization,
// but allows apiserver code to stop special-casing a nil user to skip authorization checks.
type InsecureSuperuser struct{}

func (InsecureSuperuser) AuthenticateRequest(req *http.Request) (user.Info, bool, error) {
	return &user.DefaultInfo{
		Name:   "system:unsecured",
		Groups: []string{user.SystemPrivilegedGroup, user.AllAuthenticated},
	}, true, nil
}
<|endoftext|>


<commit_before>// this provides a simple datastore for kites just with get/set methods.
// mongodb has 24k number of collection limit in a single database
// http://stackoverflow.com/questions/9858393/limits-of-number-of-collections-in-databases
// thats why we have a single collection and use single index
// though instead of using a single collection  we can use different strategies, like
// multiple database, single collections
// multiple database, multiple collections
// etc... to make it a bit more performant.
// though mongodb has an auto sharding setup, http://docs.mongodb.org/manual/sharding/
// which should be considered first. or use another datastore like elasticsearch, cassandra etc.
// to handle the sharding on database level.
// thats why we only have one strategy only for now, to get the ball rolling.

package main

import (
	"flag"
	"fmt"
	"koding/newkite/kite"
	"koding/db/mongodb/modelhelper"
)

var port = flag.String("port", "", "port to bind itself")

func main() {
	flag.Parse()

	options := &kite.Options{
		Kitename:    "datastore",
		Version:     "1",
		Port:        *port,
		Region:      "localhost",
		Environment: "development",
		PublicIP:    "127.0.0.1",
	}

	k := kite.New(options)

	k.HandleFunc("set", Set)
	k.HandleFunc("get", Get)

	k.Run()
}

func Set(r *kite.Request) (interface{}, error) {
	kv, err := r.Args.Array()
	if err != nil {
		return nil, err
	}

	keyValue := modelhelper.NewKeyValue(r.Username, r.RemoteKite.ID, kv[0].(string), kv[1].(string))
	err = modelhelper.UpsertKeyValue(keyValue)
	fmt.Println("set called with - ", kv, keyValue)
	result := true
	if err != nil {
		result = false
	}
	return result, err
}

func Get(r *kite.Request) (interface{}, error) {
	key, err := r.Args.String()
	if err != nil {
		return nil, err
	}

	fmt.Println("requesting user :", r.Username, " kite:", r.RemoteKite)
	fmt.Println("get called with - ", key)

	kv, err := modelhelper.GetKeyValue(r.Username, r.RemoteKite.ID, key)
	if err != nil{
		return err, nil
	}

	return kv.Value, nil
}
<commit_msg>kite: datastore kite: kiteid is not usable, using username, kitename, environment, key instead<commit_after>// this provides a simple datastore for kites just with get/set methods.
// mongodb has 24k number of collection limit in a single database
// http://stackoverflow.com/questions/9858393/limits-of-number-of-collections-in-databases
// thats why we have a single collection and use single index
// though instead of using a single collection  we can use different strategies, like
// multiple database, single collections
// multiple database, multiple collections
// etc... to make it a bit more performant.
// though mongodb has an auto sharding setup, http://docs.mongodb.org/manual/sharding/
// which should be considered first. or use another datastore like elasticsearch, cassandra etc.
// to handle the sharding on database level.
// thats why we only have one strategy only for now, to get the ball rolling.

package main

import (
	"flag"
	"fmt"
	"koding/newkite/kite"
	"koding/db/mongodb/modelhelper"
)

var port = flag.String("port", "", "port to bind itself")

func main() {
	flag.Parse()

	options := &kite.Options{
		Kitename:    "datastore",
		Version:     "1",
		Port:        *port,
		Region:      "localhost",
		Environment: "development",
		PublicIP:    "127.0.0.1",
	}

	k := kite.New(options)

	k.HandleFunc("set", Set)
	k.HandleFunc("get", Get)

	k.Run()
	modelhelper.EnsureKeyValueIndexes()
}

func Set(r *kite.Request) (interface{}, error) {
	kv, err := r.Args.Array()
	if err != nil {
		return nil, err
	}

	keyValue := modelhelper.NewKeyValue(r.Username, r.RemoteKite.Name, r.RemoteKite.Environment, kv[0].(string))
	keyValue.Value = kv[1].(string)
	err = modelhelper.UpsertKeyValue(keyValue)

	result := true
	if err != nil {
		result = false
	}
	return result, err
}

func Get(r *kite.Request) (interface{}, error) {
	key, err := r.Args.String()
	if err != nil {
		return nil, err
	}

	kv, err := modelhelper.GetKeyValue(r.Username, r.RemoteKite.Name, r.RemoteKite.Environment, key)
	if err != nil{
		return err, nil
	}

	return kv.Value, nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"strings"

	"github.com/dokku/dokku/plugins/common"
	"github.com/dokku/dokku/plugins/ps"
)

// main entrypoint to all subcommands
func main() {
	parts := strings.Split(os.Args[0], "/")
	subcommand := parts[len(parts)-1]

	var err error
	switch subcommand {
	case "inspect":
		args := flag.NewFlagSet("ps:inspect", flag.ExitOnError)
		args.Parse(os.Args[2:])
		appName := args.Arg(0)
		err = ps.CommandInspect(appName)
	case "rebuild":
		args := flag.NewFlagSet("ps:rebuild", flag.ExitOnError)
		allApps := args.Bool("all", false, "--all: restart all apps")
		parallelCount := args.Int("parallel", ps.RunInSerial, "--parallel: number of apps to restart in parallel, -1 to match cpu count")
		args.Parse(os.Args[2:])
		appName := args.Arg(0)
		err = ps.CommandRebuild(appName, *allApps, *parallelCount)
	case "report":
		args := flag.NewFlagSet("ps:report", flag.ExitOnError)
		args.Parse(os.Args[2:])
		appName := args.Arg(0)
		infoFlag := args.Arg(1)
		err = ps.CommandReport(appName, infoFlag)
	case "restart":
		args := flag.NewFlagSet("ps:restart", flag.ExitOnError)
		allApps := args.Bool("all", false, "--all: restart all apps")
		parallelCount := args.Int("parallel", ps.RunInSerial, "--parallel: number of apps to restart in parallel, -1 to match cpu count")
		args.Parse(os.Args[2:])
		appName := args.Arg(0)
		err = ps.CommandRestart(appName, *allApps, *parallelCount)
	case "restore":
		args := flag.NewFlagSet("ps:restore", flag.ExitOnError)
		args.Parse(os.Args[2:])
		appName := args.Arg(0)
		err = ps.CommandRestore(appName)
	case "retire":
		args := flag.NewFlagSet("ps:retire", flag.ExitOnError)
		args.Parse(os.Args[2:])
		err = ps.CommandRetire()
	case "scale":
		args := flag.NewFlagSet("ps:scale", flag.ExitOnError)
		skipDeploy := args.Bool("skip-deploy", false, "--skip-deploy: skip deploy of the app")
		args.Parse(os.Args[2:])
		appName := args.Arg(0)
		_, processTuples := common.ShiftString(args.Args())
		err = ps.CommandScale(appName, *skipDeploy, processTuples)
	case "set":
		args := flag.NewFlagSet("ps:set", flag.ExitOnError)
		args.Parse(os.Args[2:])
		appName := args.Arg(0)
		property := args.Arg(1)
		value := args.Arg(2)
		err = ps.CommandSet(appName, property, value)
	case "start":
		args := flag.NewFlagSet("ps:start", flag.ExitOnError)
		allApps := args.Bool("all", false, "--all: restart all apps")
		parallelCount := args.Int("parallel", ps.RunInSerial, "--parallel: number of apps to restart in parallel, -1 to match cpu count")
		args.Parse(os.Args[2:])
		appName := args.Arg(0)
		err = ps.CommandStart(appName, *allApps, *parallelCount)
	case "stop":
		args := flag.NewFlagSet("ps:stop", flag.ExitOnError)
		allApps := args.Bool("all", false, "--all: restart all apps")
		parallelCount := args.Int("parallel", ps.RunInSerial, "--parallel: number of apps to restart in parallel, -1 to match cpu count")
		args.Parse(os.Args[2:])
		appName := args.Arg(0)
		err = ps.CommandStop(appName, *allApps, *parallelCount)
	default:
		common.LogFail(fmt.Sprintf("Invalid plugin subcommand call: %s", subcommand))
	}

	if err != nil {
		common.LogFail(err.Error())
	}
}
<commit_msg>fix: correct help output<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"strings"

	"github.com/dokku/dokku/plugins/common"
	"github.com/dokku/dokku/plugins/ps"
)

// main entrypoint to all subcommands
func main() {
	parts := strings.Split(os.Args[0], "/")
	subcommand := parts[len(parts)-1]

	var err error
	switch subcommand {
	case "inspect":
		args := flag.NewFlagSet("ps:inspect", flag.ExitOnError)
		args.Parse(os.Args[2:])
		appName := args.Arg(0)
		err = ps.CommandInspect(appName)
	case "rebuild":
		args := flag.NewFlagSet("ps:rebuild", flag.ExitOnError)
		allApps := args.Bool("all", false, "--all: rebuild all apps")
		parallelCount := args.Int("parallel", ps.RunInSerial, "--parallel: number of apps to rebuild in parallel, -1 to match cpu count")
		args.Parse(os.Args[2:])
		appName := args.Arg(0)
		err = ps.CommandRebuild(appName, *allApps, *parallelCount)
	case "report":
		args := flag.NewFlagSet("ps:report", flag.ExitOnError)
		args.Parse(os.Args[2:])
		appName := args.Arg(0)
		infoFlag := args.Arg(1)
		err = ps.CommandReport(appName, infoFlag)
	case "restart":
		args := flag.NewFlagSet("ps:restart", flag.ExitOnError)
		allApps := args.Bool("all", false, "--all: restart all apps")
		parallelCount := args.Int("parallel", ps.RunInSerial, "--parallel: number of apps to restart in parallel, -1 to match cpu count")
		args.Parse(os.Args[2:])
		appName := args.Arg(0)
		err = ps.CommandRestart(appName, *allApps, *parallelCount)
	case "restore":
		args := flag.NewFlagSet("ps:restore", flag.ExitOnError)
		args.Parse(os.Args[2:])
		appName := args.Arg(0)
		err = ps.CommandRestore(appName)
	case "retire":
		args := flag.NewFlagSet("ps:retire", flag.ExitOnError)
		args.Parse(os.Args[2:])
		err = ps.CommandRetire()
	case "scale":
		args := flag.NewFlagSet("ps:scale", flag.ExitOnError)
		skipDeploy := args.Bool("skip-deploy", false, "--skip-deploy: skip deploy of the app")
		args.Parse(os.Args[2:])
		appName := args.Arg(0)
		_, processTuples := common.ShiftString(args.Args())
		err = ps.CommandScale(appName, *skipDeploy, processTuples)
	case "set":
		args := flag.NewFlagSet("ps:set", flag.ExitOnError)
		args.Parse(os.Args[2:])
		appName := args.Arg(0)
		property := args.Arg(1)
		value := args.Arg(2)
		err = ps.CommandSet(appName, property, value)
	case "start":
		args := flag.NewFlagSet("ps:start", flag.ExitOnError)
		allApps := args.Bool("all", false, "--all: start all apps")
		parallelCount := args.Int("parallel", ps.RunInSerial, "--parallel: number of apps to start in parallel, -1 to match cpu count")
		args.Parse(os.Args[2:])
		appName := args.Arg(0)
		err = ps.CommandStart(appName, *allApps, *parallelCount)
	case "stop":
		args := flag.NewFlagSet("ps:stop", flag.ExitOnError)
		allApps := args.Bool("all", false, "--all: stop all apps")
		parallelCount := args.Int("parallel", ps.RunInSerial, "--parallel: number of apps to stop in parallel, -1 to match cpu count")
		args.Parse(os.Args[2:])
		appName := args.Arg(0)
		err = ps.CommandStop(appName, *allApps, *parallelCount)
	default:
		common.LogFail(fmt.Sprintf("Invalid plugin subcommand call: %s", subcommand))
	}

	if err != nil {
		common.LogFail(err.Error())
	}
}
<|endoftext|>