<commit_before>// chunkbam accepts a bam file and the number of regions to split it into
// and outputs BED of intervals split evenly by number of reads. This is
// useful for parallelization. It can process a 7GB RNA-Seq file (note that this
// will have very uneven coverage) in about 1 minute, 25 seconds of user time with
// 20 CPUs. It starts outputting rows ready for parallelization in < 5 seconds.
package main

import (
	"flag"
	"fmt"
	"log"
	"math"
	"os"
	"time"

	"github.com/biogo/hts/bam"
	"github.com/biogo/hts/sam"
)

func chunkify(path string, nChunks int, minGap int) chan string {
	f, err := os.Open(path)
	if err != nil {
		log.Fatal(err)
	}
	b, err := bam.NewReader(f, 0)
	if err != nil {
		log.Fatal("error opening bam:", err)
	}
	ifr, err := os.Open(path + ".bai")
	if err != nil {
		log.Println("bam must be indexed")
		log.Fatal(err)
	}

	bai, err := bam.ReadIndex(ifr)
	log.Println("read index")
	if err != nil {
		log.Fatal(err)
	}

	refs := b.Header().Refs()
	totalMapped := uint64(0)
	for _, ref := range refs {
		s, ok := bai.ReferenceStats(ref.ID())
		if !ok {
			continue
		}
		totalMapped += s.Mapped
	}
	readsPerChunk := int(float64(totalMapped) / float64(nChunks))

	chunks := make(map[string]chan string, 8)
	// give each chrom its own channel on which to send back dat. Probably a nicer way to do this,
	// but this works...
	for _, ref := range refs {
		_, ok := bai.ReferenceStats(ref.ID())
		if !ok {
			continue
		}
		chunks[ref.Name()] = make(chan string, 32)
		go _chunkify(path, readsPerChunk, minGap, ref, bai, chunks[ref.Name()])
	}

	merged := make(chan string)
	go func() {
		for {
			anyFound := false
			for chrom := range chunks {
				if chunks[chrom] == nil {
					continue
				}
				select {
				case chunk, ok := <-chunks[chrom]:
					if !ok {
						chunks[chrom] = nil
						continue
					}
					merged <- chunk
					anyFound = true
				case <-time.After(time.Millisecond):
					anyFound = true
				}
			}
			if !anyFound {
				close(merged)
				break
			}

		}
	}()
	return merged
}
func _chunkify(path string, readsPerChunk int, minGap int, ref *sam.Reference, bai *bam.Index, ch chan string) {
	f, err := os.Open(path)
	if err != nil {
		close(ch)
		log.Fatal(err)
	}
	b, err := bam.NewReader(f, 0)
	if err != nil {
		close(ch)
		log.Fatal(err)
	}
	stats, ok := bai.ReferenceStats(ref.ID())
	if !ok {
		log.Printf("cant get stats for reference: %q. Assuming it has no reads\n", ref)
		close(ch)
		return
	}
	nReads := stats.Mapped
	if nReads == 0 {
		log.Printf("no reads mapped for: %q\n.", ref)
		close(ch)
		return
	}
	if nReads < uint64(1.2*float64(readsPerChunk)) {
		ch <- fmt.Sprintf("%s\t0\t%d\t%d", ref.Name(), ref.Len(), nReads)
		close(ch)
		return
	}

	log.Printf("chunking: %s. %d reads into %d / chunk \n", ref, nReads, readsPerChunk)

	it, err := bam.NewIterator(b, bai.Chunks(ref, 0, ref.Len()))
	if err != nil {
		log.Fatal(err)
	}

	cnt := 0
	chunk := 0
	lastStart := 0
	lastEnd := int(math.MaxInt64)
	var aln *sam.Record

	for it.Next() {
		aln = it.Record()
		if aln.Flags&sam.Unmapped != 0 {
			continue
		}
		cnt++
		// expensive to get End so only do it when needed

		if cnt >= readsPerChunk && (minGap == 0 || (minGap > 0 && aln.Start()-lastEnd > minGap)) {
			ch <- fmt.Sprintf("%s\t%d\t%d\t\t%d", ref.Name(), lastStart, aln.Start(), cnt)
			cnt = 0
			lastStart = aln.Start()
			chunk++
		}
		// only calculate end when needed since it's fairly expensive.
		if cnt >= readsPerChunk-1 && minGap > 0 {
			lastEnd = aln.End()
		}
	}
	// leftover reads from reads modulo readsPerChunk
	if cnt > 0 {
		ch <- fmt.Sprintf("%s\t%d\t%d\t\t%d", ref.Name(), lastStart, aln.Start(), cnt)
	}
	err = it.Close()
	if err != nil {
		log.Println(err)
	}
	close(ch)
}

func main() {
	chunks := flag.Int("chunks", 1000, "approximate number of chunks to split bam file")
	minGap := flag.Int("min-gap", 0, "also require this many bases between adjacent reads when ending a chunk (default is 0).")
	flag.Parse()
	bam := flag.Arg(0)
	for chunk := range chunkify(bam, *chunks, *minGap) {
		fmt.Println(chunk)
	}
}
<commit_msg>update with newer biogo interface<commit_after>// chunkbam accepts a bam file and the number of regions to split it into
// and outputs BED of intervals split evenly by number of reads. This is
// useful for parallelization. It can process a 7GB RNA-Seq file (note that this
// will have very uneven coverage) in about 1 minute, 25 seconds of user time with
// 20 CPUs. It starts outputting rows ready for parallelization in < 5 seconds.
package main

import (
	"flag"
	"fmt"
	"log"
	"math"
	"os"
	"time"

	"github.com/biogo/hts/bam"
	"github.com/biogo/hts/sam"
)

func chunkify(path string, nChunks int, minGap int) chan string {
	f, err := os.Open(path)
	if err != nil {
		log.Fatal(err)
	}
	b, err := bam.NewReader(f, 0)
	if err != nil {
		log.Fatal("error opening bam:", err)
	}
	ifr, err := os.Open(path + ".bai")
	if err != nil {
		log.Println("bam must be indexed")
		log.Fatal(err)
	}

	bai, err := bam.ReadIndex(ifr)
	log.Println("read index")
	if err != nil {
		log.Fatal(err)
	}

	refs := b.Header().Refs()
	totalMapped := uint64(0)
	for _, ref := range refs {
		s, ok := bai.ReferenceStats(ref.ID())
		if !ok {
			continue
		}
		totalMapped += s.Mapped
	}
	readsPerChunk := int(float64(totalMapped) / float64(nChunks))

	chunks := make(map[string]chan string, 8)
	// give each chrom its own channel on which to send back dat. Probably a nicer way to do this,
	// but this works...
	for _, ref := range refs {
		_, ok := bai.ReferenceStats(ref.ID())
		if !ok {
			continue
		}
		chunks[ref.Name()] = make(chan string, 32)
		go _chunkify(path, readsPerChunk, minGap, ref, bai, chunks[ref.Name()])
	}

	merged := make(chan string)
	go func() {
		for {
			anyFound := false
			for chrom := range chunks {
				if chunks[chrom] == nil {
					continue
				}
				select {
				case chunk, ok := <-chunks[chrom]:
					if !ok {
						chunks[chrom] = nil
						continue
					}
					merged <- chunk
					anyFound = true
				case <-time.After(time.Millisecond):
					anyFound = true
				}
			}
			if !anyFound {
				close(merged)
				break
			}

		}
	}()
	return merged
}
func _chunkify(path string, readsPerChunk int, minGap int, ref *sam.Reference, bai *bam.Index, ch chan string) {
	f, err := os.Open(path)
	if err != nil {
		close(ch)
		log.Fatal(err)
	}
	b, err := bam.NewReader(f, 0)
	if err != nil {
		close(ch)
		log.Fatal(err)
	}
	stats, ok := bai.ReferenceStats(ref.ID())
	if !ok {
		log.Printf("cant get stats for reference: %q. Assuming it has no reads\n", ref)
		close(ch)
		return
	}
	nReads := stats.Mapped
	if nReads == 0 {
		log.Printf("no reads mapped for: %q\n.", ref)
		close(ch)
		return
	}
	if nReads < uint64(1.2*float64(readsPerChunk)) {
		ch <- fmt.Sprintf("%s\t0\t%d\t%d", ref.Name(), ref.Len(), nReads)
		close(ch)
		return
	}

	log.Printf("chunking: %s. %d reads into %d / chunk \n", ref, nReads, readsPerChunk)

	chunks, err := bai.Chunks(ref, 0, ref.Len())
	if err != nil {
		log.Fatal(err)
	}

	it, err := bam.NewIterator(b, chunks)
	if err != nil {
		log.Fatal(err)
	}

	cnt := 0
	chunk := 0
	lastStart := 0
	lastEnd := int(math.MaxInt64)
	var aln *sam.Record

	for it.Next() {
		aln = it.Record()
		if aln.Flags&sam.Unmapped != 0 {
			continue
		}
		cnt++
		// expensive to get End so only do it when needed

		if cnt >= readsPerChunk && (minGap == 0 || (minGap > 0 && aln.Start()-lastEnd > minGap)) {
			ch <- fmt.Sprintf("%s\t%d\t%d\t\t%d", ref.Name(), lastStart, aln.Start(), cnt)
			cnt = 0
			lastStart = aln.Start()
			chunk++
		}
		// only calculate end when needed since it's fairly expensive.
		if cnt >= readsPerChunk-1 && minGap > 0 {
			lastEnd = aln.End()
		}
	}
	// leftover reads from reads modulo readsPerChunk
	if cnt > 0 {
		ch <- fmt.Sprintf("%s\t%d\t%d\t\t%d", ref.Name(), lastStart, aln.Start(), cnt)
	}
	err = it.Close()
	if err != nil {
		log.Println(err)
	}
	close(ch)
}

func main() {
	chunks := flag.Int("chunks", 1000, "approximate number of chunks to split bam file")
	minGap := flag.Int("min-gap", 0, "also require this many bases between adjacent reads when ending a chunk (default is 0).")
	flag.Parse()
	bam := flag.Arg(0)
	for chunk := range chunkify(bam, *chunks, *minGap) {
		fmt.Println(chunk)
	}
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/apigateway"
	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
	"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
)

const defaultAuthorizerTTL = 300

func resourceAwsApiGatewayAuthorizer() *schema.Resource {
	return &schema.Resource{
		Create:        resourceAwsApiGatewayAuthorizerCreate,
		Read:          resourceAwsApiGatewayAuthorizerRead,
		Update:        resourceAwsApiGatewayAuthorizerUpdate,
		Delete:        resourceAwsApiGatewayAuthorizerDelete,
		CustomizeDiff: resourceAwsApiGatewayAuthorizerCustomizeDiff,
		Importer: &schema.ResourceImporter{
			State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
				idParts := strings.Split(d.Id(), "/")
				if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" {
					return nil, fmt.Errorf("Unexpected format of ID (%q), expected REST-API-ID/AUTHORIZER-ID", d.Id())
				}
				restAPIId := idParts[0]
				authorizerId := idParts[1]
				d.Set("rest_api_id", restAPIId)
				d.SetId(authorizerId)
				return []*schema.ResourceData{d}, nil
			},
		},

		Schema: map[string]*schema.Schema{
			"authorizer_uri": {
				Type:     schema.TypeString,
				Optional: true, // authorizer_uri is required for authorizer TOKEN/REQUEST
			},
			"identity_source": {
				Type:     schema.TypeString,
				Optional: true,
				Default:  "method.request.header.Authorization",
			},
			"name": {
				Type:     schema.TypeString,
				Required: true,
			},
			"rest_api_id": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"type": {
				Type:     schema.TypeString,
				Optional: true,
				Default:  apigateway.AuthorizerTypeToken,
				ValidateFunc: validation.StringInSlice([]string{
					apigateway.AuthorizerTypeCognitoUserPools,
					apigateway.AuthorizerTypeRequest,
					apigateway.AuthorizerTypeToken,
				}, false),
			},
			"authorizer_credentials": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"authorizer_result_ttl_in_seconds": {
				Type:         schema.TypeInt,
				Optional:     true,
				ValidateFunc: validation.IntBetween(0, 3600),
				Default:      defaultAuthorizerTTL,
			},
			"identity_validation_expression": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"provider_arns": {
				Type:     schema.TypeSet,
				Optional: true, // provider_arns is required for authorizer COGNITO_USER_POOLS.
				Elem: &schema.Schema{
					Type:         schema.TypeString,
					ValidateFunc: validateArn,
				},
			},
		},
	}
}

func resourceAwsApiGatewayAuthorizerCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).apigatewayconn

	input := apigateway.CreateAuthorizerInput{
		IdentitySource: aws.String(d.Get("identity_source").(string)),
		Name:           aws.String(d.Get("name").(string)),
		RestApiId:      aws.String(d.Get("rest_api_id").(string)),
		Type:           aws.String(d.Get("type").(string)),
	}

	if err := validateAuthorizerType(d); err != nil {
		return err
	}
	if v, ok := d.GetOk("authorizer_uri"); ok {
		input.AuthorizerUri = aws.String(v.(string))
	}
	if v, ok := d.GetOk("authorizer_credentials"); ok {
		input.AuthorizerCredentials = aws.String(v.(string))
	}
	if v, ok := d.GetOkExists("authorizer_result_ttl_in_seconds"); ok {
		input.AuthorizerResultTtlInSeconds = aws.Int64(int64(v.(int)))
	}
	if v, ok := d.GetOk("identity_validation_expression"); ok {
		input.IdentityValidationExpression = aws.String(v.(string))
	}
	if v, ok := d.GetOk("provider_arns"); ok {
		input.ProviderARNs = expandStringList(v.(*schema.Set).List())
	}

	log.Printf("[INFO] Creating API Gateway Authorizer: %s", input)
	out, err := conn.CreateAuthorizer(&input)
	if err != nil {
		return fmt.Errorf("Error creating API Gateway Authorizer: %s", err)
	}

	d.SetId(*out.Id)

	return resourceAwsApiGatewayAuthorizerRead(d, meta)
}

func resourceAwsApiGatewayAuthorizerRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).apigatewayconn

	log.Printf("[INFO] Reading API Gateway Authorizer %s", d.Id())
	input := apigateway.GetAuthorizerInput{
		AuthorizerId: aws.String(d.Id()),
		RestApiId:    aws.String(d.Get("rest_api_id").(string)),
	}

	authorizer, err := conn.GetAuthorizer(&input)
	if err != nil {
		if isAWSErr(err, apigateway.ErrCodeNotFoundException, "") {
			log.Printf("[WARN] No API Gateway Authorizer found: %s", input)
			d.SetId("")
			return nil
		}
		return err
	}
	log.Printf("[DEBUG] Received API Gateway Authorizer: %s", authorizer)

	d.Set("authorizer_credentials", authorizer.AuthorizerCredentials)

	if authorizer.AuthorizerResultTtlInSeconds != nil {
		d.Set("authorizer_result_ttl_in_seconds", authorizer.AuthorizerResultTtlInSeconds)
	} else {
		d.Set("authorizer_result_ttl_in_seconds", defaultAuthorizerTTL)
	}

	d.Set("authorizer_uri", authorizer.AuthorizerUri)
	d.Set("identity_source", authorizer.IdentitySource)
	d.Set("identity_validation_expression", authorizer.IdentityValidationExpression)
	d.Set("name", authorizer.Name)
	d.Set("type", authorizer.Type)
	d.Set("provider_arns", flattenStringList(authorizer.ProviderARNs))

	return nil
}

func resourceAwsApiGatewayAuthorizerUpdate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).apigatewayconn

	input := apigateway.UpdateAuthorizerInput{
		AuthorizerId: aws.String(d.Id()),
		RestApiId:    aws.String(d.Get("rest_api_id").(string)),
	}

	operations := make([]*apigateway.PatchOperation, 0)

	if d.HasChange("authorizer_uri") {
		operations = append(operations, &apigateway.PatchOperation{
			Op:    aws.String(apigateway.OpReplace),
			Path:  aws.String("/authorizerUri"),
			Value: aws.String(d.Get("authorizer_uri").(string)),
		})
	}
	if d.HasChange("identity_source") {
		operations = append(operations, &apigateway.PatchOperation{
			Op:    aws.String(apigateway.OpReplace),
			Path:  aws.String("/identitySource"),
			Value: aws.String(d.Get("identity_source").(string)),
		})
	}
	if d.HasChange("name") {
		operations = append(operations, &apigateway.PatchOperation{
			Op:    aws.String(apigateway.OpReplace),
			Path:  aws.String("/name"),
			Value: aws.String(d.Get("name").(string)),
		})
	}
	if d.HasChange("type") {
		operations = append(operations, &apigateway.PatchOperation{
			Op:    aws.String(apigateway.OpReplace),
			Path:  aws.String("/type"),
			Value: aws.String(d.Get("type").(string)),
		})
	}
	if d.HasChange("authorizer_credentials") {
		operations = append(operations, &apigateway.PatchOperation{
			Op:    aws.String(apigateway.OpReplace),
			Path:  aws.String("/authorizerCredentials"),
			Value: aws.String(d.Get("authorizer_credentials").(string)),
		})
	}
	if d.HasChange("authorizer_result_ttl_in_seconds") {
		operations = append(operations, &apigateway.PatchOperation{
			Op:    aws.String(apigateway.OpReplace),
			Path:  aws.String("/authorizerResultTtlInSeconds"),
			Value: aws.String(fmt.Sprintf("%d", d.Get("authorizer_result_ttl_in_seconds").(int))),
		})
	}
	if d.HasChange("identity_validation_expression") {
		operations = append(operations, &apigateway.PatchOperation{
			Op:    aws.String(apigateway.OpReplace),
			Path:  aws.String("/identityValidationExpression"),
			Value: aws.String(d.Get("identity_validation_expression").(string)),
		})
	}
	if d.HasChange("provider_arns") {
		old, new := d.GetChange("provider_arns")
		os := old.(*schema.Set)
		ns := new.(*schema.Set)
		// providerARNs can't be empty, so add first and then remove
		additionList := ns.Difference(os)
		for _, v := range additionList.List() {
			operations = append(operations, &apigateway.PatchOperation{
				Op:    aws.String(apigateway.OpAdd),
				Path:  aws.String("/providerARNs"),
				Value: aws.String(v.(string)),
			})
		}
		removalList := os.Difference(ns)
		for _, v := range removalList.List() {
			operations = append(operations, &apigateway.PatchOperation{
				Op:    aws.String(apigateway.OpRemove),
				Path:  aws.String("/providerARNs"),
				Value: aws.String(v.(string)),
			})
		}
	}

	input.PatchOperations = operations

	log.Printf("[INFO] Updating API Gateway Authorizer: %s", input)
	_, err := conn.UpdateAuthorizer(&input)
	if err != nil {
		return fmt.Errorf("Updating API Gateway Authorizer failed: %s", err)
	}

	return resourceAwsApiGatewayAuthorizerRead(d, meta)
}

func resourceAwsApiGatewayAuthorizerDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).apigatewayconn
	input := apigateway.DeleteAuthorizerInput{
		AuthorizerId: aws.String(d.Id()),
		RestApiId:    aws.String(d.Get("rest_api_id").(string)),
	}
	log.Printf("[INFO] Deleting API Gateway Authorizer: %s", input)
	_, err := conn.DeleteAuthorizer(&input)
	if err != nil {
		// XXX: Figure out a way to delete the method that depends on the authorizer first
		// otherwise the authorizer will be dangling until the API is deleted
		if !strings.Contains(err.Error(), apigateway.ErrCodeConflictException) {
			return fmt.Errorf("Deleting API Gateway Authorizer failed: %s", err)
		}
	}

	return nil
}

func resourceAwsApiGatewayAuthorizerCustomizeDiff(diff *schema.ResourceDiff, v interface{}) error {
	// switch type between COGNITO_USER_POOLS and TOKEN/REQUEST will create new resource.
	if diff.HasChange("type") {
		o, n := diff.GetChange("type")
		if o.(string) == apigateway.AuthorizerTypeCognitoUserPools || n.(string) == apigateway.AuthorizerTypeCognitoUserPools {
			if err := diff.ForceNew("type"); err != nil {
				return err
			}
		}
	}

	return nil
}

func validateAuthorizerType(d *schema.ResourceData) error {
	authType := d.Get("type").(string)
	// authorizer_uri is required for authorizer TOKEN/REQUEST
	if authType == apigateway.AuthorizerTypeRequest || authType == apigateway.AuthorizerTypeToken {
		if v, ok := d.GetOk("authorizer_uri"); !ok || v.(string) == "" {
			return fmt.Errorf("authorizer_uri must be set non-empty when authorizer type is %s", authType)
		}
	}
	// provider_arns is required for authorizer COGNITO_USER_POOLS.
	if authType == apigateway.AuthorizerTypeCognitoUserPools {
		if v, ok := d.GetOk("provider_arns"); !ok || len(v.(*schema.Set).List()) == 0 {
			return fmt.Errorf("provider_arns must be set non-empty when authorizer type is %s", authType)
		}
	}

	return nil
}
<commit_msg>add validation for `authorizer_uri`, `authorizer_credentials` changes for %w remove deprecated func<commit_after>package aws

import (
	"fmt"
	"log"
	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/apigateway"
	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
	"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
)

const defaultAuthorizerTTL = 300

func resourceAwsApiGatewayAuthorizer() *schema.Resource {
	return &schema.Resource{
		Create:        resourceAwsApiGatewayAuthorizerCreate,
		Read:          resourceAwsApiGatewayAuthorizerRead,
		Update:        resourceAwsApiGatewayAuthorizerUpdate,
		Delete:        resourceAwsApiGatewayAuthorizerDelete,
		CustomizeDiff: resourceAwsApiGatewayAuthorizerCustomizeDiff,
		Importer: &schema.ResourceImporter{
			State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
				idParts := strings.Split(d.Id(), "/")
				if len(idParts) != 2 || idParts[0] == "" || idParts[1] == "" {
					return nil, fmt.Errorf("Unexpected format of ID (%q), expected REST-API-ID/AUTHORIZER-ID", d.Id())
				}
				restAPIId := idParts[0]
				authorizerId := idParts[1]
				d.Set("rest_api_id", restAPIId)
				d.SetId(authorizerId)
				return []*schema.ResourceData{d}, nil
			},
		},

		Schema: map[string]*schema.Schema{
			"authorizer_uri": {
				Type:         schema.TypeString,
				Optional:     true, // authorizer_uri is required for authorizer TOKEN/REQUEST
				ValidateFunc: validateArn,
			},
			"identity_source": {
				Type:     schema.TypeString,
				Optional: true,
				Default:  "method.request.header.Authorization",
			},
			"name": {
				Type:     schema.TypeString,
				Required: true,
			},
			"rest_api_id": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"type": {
				Type:     schema.TypeString,
				Optional: true,
				Default:  apigateway.AuthorizerTypeToken,
				ValidateFunc: validation.StringInSlice([]string{
					apigateway.AuthorizerTypeCognitoUserPools,
					apigateway.AuthorizerTypeRequest,
					apigateway.AuthorizerTypeToken,
				}, false),
			},
			"authorizer_credentials": {
				Type:         schema.TypeString,
				Optional:     true,
				ValidateFunc: validateArn,
			},
			"authorizer_result_ttl_in_seconds": {
				Type:         schema.TypeInt,
				Optional:     true,
				ValidateFunc: validation.IntBetween(0, 3600),
				Default:      defaultAuthorizerTTL,
			},
			"identity_validation_expression": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"provider_arns": {
				Type:     schema.TypeSet,
				Optional: true, // provider_arns is required for authorizer COGNITO_USER_POOLS.
				Elem: &schema.Schema{
					Type:         schema.TypeString,
					ValidateFunc: validateArn,
				},
			},
		},
	}
}

func resourceAwsApiGatewayAuthorizerCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).apigatewayconn

	input := apigateway.CreateAuthorizerInput{
		IdentitySource:               aws.String(d.Get("identity_source").(string)),
		Name:                         aws.String(d.Get("name").(string)),
		RestApiId:                    aws.String(d.Get("rest_api_id").(string)),
		Type:                         aws.String(d.Get("type").(string)),
		AuthorizerResultTtlInSeconds: aws.Int64(int64(d.Get("authorizer_result_ttl_in_seconds").(int))),
	}

	if err := validateAuthorizerType(d); err != nil {
		return err
	}
	if v, ok := d.GetOk("authorizer_uri"); ok {
		input.AuthorizerUri = aws.String(v.(string))
	}
	if v, ok := d.GetOk("authorizer_credentials"); ok {
		input.AuthorizerCredentials = aws.String(v.(string))
	}

	if v, ok := d.GetOk("identity_validation_expression"); ok {
		input.IdentityValidationExpression = aws.String(v.(string))
	}
	if v, ok := d.GetOk("provider_arns"); ok {
		input.ProviderARNs = expandStringSet(v.(*schema.Set))
	}

	log.Printf("[INFO] Creating API Gateway Authorizer: %s", input)
	out, err := conn.CreateAuthorizer(&input)
	if err != nil {
		return fmt.Errorf("error creating API Gateway Authorizer: %w", err)
	}

	d.SetId(aws.StringValue(out.Id))

	return resourceAwsApiGatewayAuthorizerRead(d, meta)
}

func resourceAwsApiGatewayAuthorizerRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).apigatewayconn

	log.Printf("[INFO] Reading API Gateway Authorizer %s", d.Id())
	input := apigateway.GetAuthorizerInput{
		AuthorizerId: aws.String(d.Id()),
		RestApiId:    aws.String(d.Get("rest_api_id").(string)),
	}

	authorizer, err := conn.GetAuthorizer(&input)
	if err != nil {
		if isAWSErr(err, apigateway.ErrCodeNotFoundException, "") {
			log.Printf("[WARN] No API Gateway Authorizer found: %s", input)
			d.SetId("")
			return nil
		}
		return err
	}
	log.Printf("[DEBUG] Received API Gateway Authorizer: %s", authorizer)

	d.Set("authorizer_credentials", authorizer.AuthorizerCredentials)

	if authorizer.AuthorizerResultTtlInSeconds != nil {
		d.Set("authorizer_result_ttl_in_seconds", authorizer.AuthorizerResultTtlInSeconds)
	} else {
		d.Set("authorizer_result_ttl_in_seconds", defaultAuthorizerTTL)
	}

	d.Set("authorizer_uri", authorizer.AuthorizerUri)
	d.Set("identity_source", authorizer.IdentitySource)
	d.Set("identity_validation_expression", authorizer.IdentityValidationExpression)
	d.Set("name", authorizer.Name)
	d.Set("type", authorizer.Type)
	d.Set("provider_arns", flattenStringSet(authorizer.ProviderARNs))

	return nil
}

func resourceAwsApiGatewayAuthorizerUpdate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).apigatewayconn

	input := apigateway.UpdateAuthorizerInput{
		AuthorizerId: aws.String(d.Id()),
		RestApiId:    aws.String(d.Get("rest_api_id").(string)),
	}

	operations := make([]*apigateway.PatchOperation, 0)

	if d.HasChange("authorizer_uri") {
		operations = append(operations, &apigateway.PatchOperation{
			Op:    aws.String(apigateway.OpReplace),
			Path:  aws.String("/authorizerUri"),
			Value: aws.String(d.Get("authorizer_uri").(string)),
		})
	}
	if d.HasChange("identity_source") {
		operations = append(operations, &apigateway.PatchOperation{
			Op:    aws.String(apigateway.OpReplace),
			Path:  aws.String("/identitySource"),
			Value: aws.String(d.Get("identity_source").(string)),
		})
	}
	if d.HasChange("name") {
		operations = append(operations, &apigateway.PatchOperation{
			Op:    aws.String(apigateway.OpReplace),
			Path:  aws.String("/name"),
			Value: aws.String(d.Get("name").(string)),
		})
	}
	if d.HasChange("type") {
		operations = append(operations, &apigateway.PatchOperation{
			Op:    aws.String(apigateway.OpReplace),
			Path:  aws.String("/type"),
			Value: aws.String(d.Get("type").(string)),
		})
	}
	if d.HasChange("authorizer_credentials") {
		operations = append(operations, &apigateway.PatchOperation{
			Op:    aws.String(apigateway.OpReplace),
			Path:  aws.String("/authorizerCredentials"),
			Value: aws.String(d.Get("authorizer_credentials").(string)),
		})
	}
	if d.HasChange("authorizer_result_ttl_in_seconds") {
		operations = append(operations, &apigateway.PatchOperation{
			Op:    aws.String(apigateway.OpReplace),
			Path:  aws.String("/authorizerResultTtlInSeconds"),
			Value: aws.String(fmt.Sprintf("%d", d.Get("authorizer_result_ttl_in_seconds").(int))),
		})
	}
	if d.HasChange("identity_validation_expression") {
		operations = append(operations, &apigateway.PatchOperation{
			Op:    aws.String(apigateway.OpReplace),
			Path:  aws.String("/identityValidationExpression"),
			Value: aws.String(d.Get("identity_validation_expression").(string)),
		})
	}
	if d.HasChange("provider_arns") {
		old, new := d.GetChange("provider_arns")
		os := old.(*schema.Set)
		ns := new.(*schema.Set)
		// providerARNs can't be empty, so add first and then remove
		additionList := ns.Difference(os)
		for _, v := range additionList.List() {
			operations = append(operations, &apigateway.PatchOperation{
				Op:    aws.String(apigateway.OpAdd),
				Path:  aws.String("/providerARNs"),
				Value: aws.String(v.(string)),
			})
		}
		removalList := os.Difference(ns)
		for _, v := range removalList.List() {
			operations = append(operations, &apigateway.PatchOperation{
				Op:    aws.String(apigateway.OpRemove),
				Path:  aws.String("/providerARNs"),
				Value: aws.String(v.(string)),
			})
		}
	}

	input.PatchOperations = operations

	log.Printf("[INFO] Updating API Gateway Authorizer: %s", input)
	_, err := conn.UpdateAuthorizer(&input)
	if err != nil {
		return fmt.Errorf("updating API Gateway Authorizer failed: %w", err)
	}

	return resourceAwsApiGatewayAuthorizerRead(d, meta)
}

func resourceAwsApiGatewayAuthorizerDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).apigatewayconn
	input := apigateway.DeleteAuthorizerInput{
		AuthorizerId: aws.String(d.Id()),
		RestApiId:    aws.String(d.Get("rest_api_id").(string)),
	}
	log.Printf("[INFO] Deleting API Gateway Authorizer: %s", input)
	_, err := conn.DeleteAuthorizer(&input)
	if err != nil {
		// XXX: Figure out a way to delete the method that depends on the authorizer first
		// otherwise the authorizer will be dangling until the API is deleted
		if !strings.Contains(err.Error(), apigateway.ErrCodeConflictException) {
			return fmt.Errorf("deleting API Gateway Authorizer failed: %w", err)
		}
	}

	return nil
}

func resourceAwsApiGatewayAuthorizerCustomizeDiff(diff *schema.ResourceDiff, v interface{}) error {
	// switch type between COGNITO_USER_POOLS and TOKEN/REQUEST will create new resource.
	if diff.HasChange("type") {
		o, n := diff.GetChange("type")
		if o.(string) == apigateway.AuthorizerTypeCognitoUserPools || n.(string) == apigateway.AuthorizerTypeCognitoUserPools {
			if err := diff.ForceNew("type"); err != nil {
				return err
			}
		}
	}

	return nil
}

func validateAuthorizerType(d *schema.ResourceData) error {
	authType := d.Get("type").(string)
	// authorizer_uri is required for authorizer TOKEN/REQUEST
	if authType == apigateway.AuthorizerTypeRequest || authType == apigateway.AuthorizerTypeToken {
		if v, ok := d.GetOk("authorizer_uri"); !ok || v.(string) == "" {
			return fmt.Errorf("authorizer_uri must be set non-empty when authorizer type is %s", authType)
		}
	}
	// provider_arns is required for authorizer COGNITO_USER_POOLS.
	if authType == apigateway.AuthorizerTypeCognitoUserPools {
		if v, ok := d.GetOk("provider_arns"); !ok || len(v.(*schema.Set).List()) == 0 {
			return fmt.Errorf("provider_arns must be set non-empty when authorizer type is %s", authType)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package config

type ScrapeConfig struct {
	URL             string `json:"scrape_url"`
	Name            string `json:"name"`
	IntervalSeconds int64  `json:"interval"`
}

type ScraperConfig struct {
	Addr            string         `json:"bind"`
	NumWorkers      int            `json:"workers"`
	IntervalSeconds int64          `json:"default_interval"`
	Trackers        []ScrapeConfig `json:"trackers"`
}

// DefaultScraperConfig is the default config for bittorrent scraping
var DefaultScraperConfig = ScraperConfig{
	Addr: ":9999",
	// TODO: query system?
	NumWorkers: 4,
	// every hour
	IntervalSeconds: 60 * 60,
	Trackers: []ScrapeConfig{
		ScrapeConfig{
			URL:  "udp://tracker.doko.moe:6969/",
			Name: "doko.moe",
		},
		ScrapeConfig{
			URL:  "udp://tracker.coppersurfer.tk:6969/",
			Name: "coppersurfer.tk",
		},
	},
}
<commit_msg>only use coppersurfer<commit_after>package config

type ScrapeConfig struct {
	URL             string `json:"scrape_url"`
	Name            string `json:"name"`
	IntervalSeconds int64  `json:"interval"`
}

type ScraperConfig struct {
	Addr            string         `json:"bind"`
	NumWorkers      int            `json:"workers"`
	IntervalSeconds int64          `json:"default_interval"`
	Trackers        []ScrapeConfig `json:"trackers"`
}

// DefaultScraperConfig is the default config for bittorrent scraping
var DefaultScraperConfig = ScraperConfig{
	Addr: ":9999",
	// TODO: query system?
	NumWorkers: 4,
	// every hour
	IntervalSeconds: 60 * 60,
	Trackers: []ScrapeConfig{
		ScrapeConfig{
			URL:  "udp://tracker.coppersurfer.tk:6969/",
			Name: "coppersurfer.tk",
		},
	},
}
<|endoftext|>


<commit_before>package config

import (
	"encoding/json"
	"io/ioutil"
)

// Search represents a search term used for advancing through the entries of the same
// term.
type Search struct {
	Term  string
	Index int
}

// ReadSearch reads the last saved search entry.
//
// If the last search doesn't exist, a zero value Search is returned.
func (c *Config) ReadSearch() (search Search) {
	searchFile, err := c.searchFile()
	if err != nil {
		return
	}

	defer closeLockedFile(searchFile)

	if content, err := ioutil.ReadAll(searchFile); err == nil {
		if err := json.Unmarshal(content, &search); err == nil {
			return
		}
	}

	return
}

// WriteSearch writes the last search entry to the current search entry.
func (c *Config) WriteSearch(term string, index int) error {
	searchFile, err := c.searchFile()
	if err != nil {
		return err
	}

	defer closeLockedFile(searchFile)

	jsonContent, err := json.Marshal(&Search{term, index})
	if err != nil {
		return err
	}

	_, ferr := searchFile.Write(jsonContent)
	return ferr
}
<commit_msg>Avoid bad search file JSON writes<commit_after>package config

import (
	"encoding/json"
	"io/ioutil"
)

// Search represents a search term used for advancing through the entries of the same
// term.
type Search struct {
	Term  string
	Index int
}

// ReadSearch reads the last saved search entry.
//
// If the last search doesn't exist, a zero value Search is returned.
func (c *Config) ReadSearch() (search Search) {
	searchFile, err := c.searchFile()
	if err != nil {
		return
	}

	defer closeLockedFile(searchFile)

	if content, err := ioutil.ReadAll(searchFile); err == nil {
		if err := json.Unmarshal(content, &search); err == nil {
			return
		}
	}

	return
}

// WriteSearch writes the last search entry to the current search entry.
func (c *Config) WriteSearch(term string, index int) error {
	searchFile, err := c.searchFile()
	if err != nil {
		return err
	}

	defer closeLockedFile(searchFile)

	jsonContent, err := json.Marshal(&Search{term, index})
	if err != nil {
		return err
	}

	if err := searchFile.Truncate(0); err != nil {
		return err
	}

	_, err = searchFile.Write(jsonContent)
	return err
}
<|endoftext|>


<commit_before>package plugin

import (
	"os"
	"testing"
	"time"

	"github.com/josledp/termcolor"
)

func TestAws(t *testing.T) {

	os.Setenv("AWS_ROLE", "test:xx-yy-zz")
	os.Setenv("AWS_SESSION_EXPIRE", "1506345326")
	expected := "\\[\\033[0m\\]\\[\\033[31m\\]test:zz\\[\\033[0m\\]"

	a := &Aws{}
	a.Load(nil)

	if a.role != "test:zz" {
		t.Error("Invalid AWS role")
	}

	if !(time.Unix(1506345326, int64(0)).Equal(a.expire)) {
		t.Error("Invalid AWS expire time")
	}
	output, _ := a.Get(termcolor.EscapedFormat)
	if output != expected {
		t.Errorf("Expected %s\nGot      %s", expected, output)
	}

}
<commit_msg>fix aws_test<commit_after>package plugin

import (
	"os"
	"testing"
	"time"

	"github.com/josledp/termcolor"
)

func TestAws(t *testing.T) {

	os.Setenv("AWS_ROLE", "test:xx-yy-zz")
	os.Setenv("AWS_SESSION_EXPIRE", "1506345326")
	expectedPrompt := "\\[\\033[0m\\]\\[\\033[31m\\]test:xx-yy-zz\\[\\033[0m\\]"
	expectedRole := "test:xx-yy-zz"

	a := &Aws{}
	a.Load(nil)

	if a.role != expectedRole {
		t.Error("Expected role %s, got %s", expectedRole, a.role)
	}

	if !(time.Unix(1506345326, int64(0)).Equal(a.expire)) {
		t.Error("AWS expire time error. expected %d, got %d", 1506345326, a.expire)
	}
	output, _ := a.Get(termcolor.EscapedFormat)
	if output != expectedPrompt {
		t.Errorf("Expected %s\nGot      %s", expectedPrompt, output)
	}

}
<|endoftext|>


<commit_before>package quadtree

import (
	"math"
	//"log"
	//"runtime"
	//"sync"
	//"image"
	//"github.com/nfnt/resize"

	"fmt"

	"github.com/gnayuy/datamapper/node"
)

 // Quadrant ordering (Z-order curve):
 //    +––––––+––––––+
 //    |      |      |
 //    |TL(00)|TR(01)|
 //    |      |      |
 //    +–––––––––––––+
 //    |      |      |
 //    |BL(10)|BR(11)|
 //    |      |      |
 //    +––––––+––––––+

const (
	qtW=512
	qtH=512
)

type QuadTree struct {

	depth		int
	level		int

	isLeaf		bool
	dataAvail	bool // in immutable storage ?

	parent		*QuadTree

	TL			*QuadTree // 00
	TR			*QuadTree // 01
	BL			*QuadTree // 10
	BR			*QuadTree // 11

	node 		*node.Node

}

// VoxelSize, MinPoint, MaxPoint
func Init(xmin, xmax, ymin, ymax, zmin, zmax int64, resx, resy, resz float64) *QuadTree {

	var qt *QuadTree

	dimx := xmax - xmin + 1
	dimy := ymax - ymin + 1

	depthx := int(math.Log2(float64(dimx))+0.5)-int(math.Log2(float64(qtW)))+1
	depthy := int(math.Log2(float64(dimy))+0.5)-int(math.Log2(float64(qtH)))+1

	depth := int(math.Max(float64(depthx), float64(depthy)))

	fmt.Println("The depth of this quadtree is ", depth)

	ch := make(chan bool)
	go func() {
	    Construct(nil,qt,depth,0,xmin,ymin,zmin,resx,resy,resz,0,0,0,qtW,qtH,1,ch)
	    ch <- false
	}()
	<- ch

	return qt
}

func Construct(parent,root *QuadTree, depth,level int, xmin,ymin,zmin int64, resx,resy,resz float64, cx,cy,cz,w,h,d int64, ch chan bool) {

	depth = depth - 1
	level = level + 1

	if(depth < 0){
		ch <- true
		return

	}else{

		root = &QuadTree{depth,level,false,false,parent,nil,nil,nil,nil,nil}

		root.node = node.NewNode(cx,cy,cz,w,h,d,resx,resy,resz,xmin,ymin,zmin,nil)
		root.depth = depth + 1

		root.dataAvail = false

		if(depth==0){
			root.isLeaf = true
		}else{
			root.isLeaf = false
		}

		root.parent = parent

		resx = resx / 2.0
		resy = resy / 2.0
		resz = resz / 2.0

		cx = cx * 2
		cy = cy * 2
		cz = cz * 2

		go Construct(root,root.TL,depth,level,xmin,ymin,zmin,resx,resy,resz,cx,  cy,  cz,w,h,d,ch)
		go Construct(root,root.TR,depth,level,xmin,ymin,zmin,resx,resy,resz,cx+1,cy,  cz,w,h,d,ch)
		go Construct(root,root.BL,depth,level,xmin,ymin,zmin,resx,resy,resz,cx,  cy+1,cz,w,h,d,ch)
		go Construct(root,root.BR,depth,level,xmin,ymin,zmin,resx,resy,resz,cx+1,cy+1,cz,w,h,d,ch)

	}

}

func GetData(qt *QuadTree, ch chan bool) {
	// if it is leaf, get the data from database
	// else get the data from its childtren and then resize

	if(qt.dataAvail==true){
		ch <- true
		return;
	}else{

		if(qt.isLeaf==true){
			// get data

			// save data if not empty

		}else{
			// get data from its children's data

			go GetData(qt.TL,ch)
			go GetData(qt.TR,ch)
			go GetData(qt.BL,ch)
			go GetData(qt.BR,ch)

			// resize

			// save data
		}

		qt.dataAvail = true;
	}

}

<commit_msg>update quadtree parallel generating<commit_after>package quadtree

import (
	"math"
	//"log"
	//"runtime"
	//"sync"
	//"image"
	//"github.com/nfnt/resize"

	"fmt"

	"github.com/gnayuy/datamapper/node"
)

 // Quadrant ordering (Z-order curve):
 //    +––––––+––––––+
 //    |      |      |
 //    |TL(00)|TR(01)|
 //    |      |      |
 //    +–––––––––––––+
 //    |      |      |
 //    |BL(10)|BR(11)|
 //    |      |      |
 //    +––––––+––––––+

const (
	qtW=512
	qtH=512
)

type QuadTree struct {

	depth		int
	level		int

	isLeaf		bool
	dataAvail	bool // in immutable storage ?

	parent		*QuadTree

	TL			*QuadTree // 00
	TR			*QuadTree // 01
	BL			*QuadTree // 10
	BR			*QuadTree // 11

	node 		*node.Node

}

type empty struct {}
type semaphore chan empty

// VoxelSize, MinPoint, MaxPoint
func Init(xmin, xmax, ymin, ymax, zmin, zmax int64, resx, resy, resz float64) *[]QuadTree {

	dimz := zmax - zmin + 1

	qt := make([]QuadTree, dimz)

	dimx := xmax - xmin + 1
	dimy := ymax - ymin + 1

	depthx := int(math.Log2(float64(dimx))+0.5)-int(math.Log2(float64(qtW)))+1
	depthy := int(math.Log2(float64(dimy))+0.5)-int(math.Log2(float64(qtH)))+1

	depth := int(math.Max(float64(depthx), float64(depthy)))

	fmt.Println("The depth of this quadtree is ", depth)

	//go func() {
	//    Construct(nil,qt,depth,0,xmin,ymin,zmin,resx,resy,resz,0,0,0,qtW,qtH,1,ch)
	//    ch <- false
	//}()
	//<- ch

	sem := make (semaphore, dimz);
	for z := zmin; z < zmax; z++ {
		ch := make(chan bool)
        go func (z int64) {
            Construct(nil,qt[z],depth,0,xmin,ymin,z,resx,resy,resz,0,0,0,qtW,qtH,1,ch)
            sem <- empty{};
        } (z);
    }
    for z := zmin; z < zmax; z++ {
		<- sem // release dimz resources
	}

	return qt
}

func Construct(parent,root *QuadTree, depth,level int, xmin,ymin,zmin int64, resx,resy,resz float64, cx,cy,cz,w,h,d int64, ch chan bool) {

	depth = depth - 1
	level = level + 1

	if(depth < 0){
		ch <- true
		return

	}else{

		root = &QuadTree{depth,level,false,false,parent,nil,nil,nil,nil,nil}

		root.node = node.NewNode(cx,cy,cz,w,h,d,resx,resy,resz,xmin,ymin,zmin,nil)
		root.depth = depth + 1

		root.dataAvail = false

		if(depth==0){
			root.isLeaf = true
		}else{
			root.isLeaf = false
		}

		root.parent = parent

		resx = resx / 2.0
		resy = resy / 2.0
		resz = resz / 2.0

		cx = cx * 2
		cy = cy * 2
		cz = cz * 2

		go Construct(root,root.TL,depth,level,xmin,ymin,zmin,resx,resy,resz,cx,  cy,  cz,w,h,d,ch)
		go Construct(root,root.TR,depth,level,xmin,ymin,zmin,resx,resy,resz,cx+1,cy,  cz,w,h,d,ch)
		go Construct(root,root.BL,depth,level,xmin,ymin,zmin,resx,resy,resz,cx,  cy+1,cz,w,h,d,ch)
		go Construct(root,root.BR,depth,level,xmin,ymin,zmin,resx,resy,resz,cx+1,cy+1,cz,w,h,d,ch)

	}

}

func GetData(qt *QuadTree, ch chan bool) {
	// if it is leaf, get the data from database
	// else get the data from its childtren and then resize

	if(qt.dataAvail==true){
		ch <- true
		return;
	}else{

		if(qt.isLeaf==true){
			// get data

			// save data if not empty

		}else{
			// get data from its children's data

			go GetData(qt.TL,ch)
			go GetData(qt.TR,ch)
			go GetData(qt.BL,ch)
			go GetData(qt.BR,ch)

			// resize

			// save data
		}

		qt.dataAvail = true;
	}

}

<|endoftext|>


<commit_before>/*
Copyright 2015 Gravitational, Inc.

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

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

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

package common

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

	"github.com/gravitational/kingpin"
	"github.com/gravitational/teleport"
	"github.com/gravitational/teleport/lib/auth"
	"github.com/gravitational/teleport/lib/defaults"
	"github.com/gravitational/teleport/lib/service"
	"github.com/gravitational/trace"
)

// NodeCommand implements `tctl nodes` group of commands
type NodeCommand struct {
	config *service.Config
	// format is the output format, e.g. text or json
	format string
	// list of roles for the new node to assume
	roles string
	// TTL: duration of time during which a generated node token will
	// be valid.
	ttl time.Duration
	// namespace is node namespace
	namespace string
	// token is an optional custom token supplied by client,
	// if not specified, is autogenerated
	token string

	// CLI subcommands (clauses)
	nodeAdd  *kingpin.CmdClause
	nodeList *kingpin.CmdClause
}

// Initialize allows NodeCommand to plug itself into the CLI parser
func (c *NodeCommand) Initialize(app *kingpin.Application, config *service.Config) {
	c.config = config

	// add node command
	nodes := app.Command("nodes", "Issue invites for other nodes to join the cluster")
	c.nodeAdd = nodes.Command("add", "Generate a node invitation token")
	c.nodeAdd.Flag("roles", "Comma-separated list of roles for the new node to assume [node]").Default("node").StringVar(&c.roles)
	c.nodeAdd.Flag("ttl", "Time to live for a generated token").Default(defaults.ProvisioningTokenTTL.String()).DurationVar(&c.ttl)
	c.nodeAdd.Flag("token", "Custom token to use, autogenerated if not provided").StringVar(&c.token)
	c.nodeAdd.Flag("format", "Output format, 'text' or 'json'").Hidden().Default("text").StringVar(&c.format)
	c.nodeAdd.Alias(AddNodeHelp)

	c.nodeList = nodes.Command("ls", "List all active SSH nodes within the cluster")
	c.nodeList.Flag("namespace", "Namespace of the nodes").Default(defaults.Namespace).StringVar(&c.namespace)
	c.nodeList.Alias(ListNodesHelp)
}

// TryRun takes the CLI command as an argument (like "nodes ls") and executes it.
func (c *NodeCommand) TryRun(cmd string, client auth.ClientI) (match bool, err error) {
	switch cmd {
	case c.nodeAdd.FullCommand():
		err = c.Invite(client)
	case c.nodeList.FullCommand():
		err = c.ListActive(client)

	default:
		return false, nil
	}
	return true, trace.Wrap(err)
}

const trustedClusterTemplate = `kind: trusted_cluster
version: v2
metadata:
   name: %v
spec:
   enabled: true
   token: %v
   web_proxy_addr: proxy.example.com:3080
   role_map:
   - remote: admin
     local: [admin]`

const trustedClusterMessage = `Trusted cluster token: %v

Use this cluster in trusted cluster resource, for example:

%v

Please note:

  - This token will expire in %d minutes.
  - Replace address proxy.example.com:3080 with externally accessible teleport proxy address.
  - Set proper local and remote role_map property.
`

const nodeMessage = `The invite token: %v

Run this on the new node to join the cluster:

> teleport start --roles=%s --token=%v --auth-server=%v

Please note:

  - This invitation token will expire in %d minutes
  - %v must be reachable from the new node, see --advertise-ip server flag
`

// Invite generates a token which can be used to add another SSH node
// to a cluster
func (c *NodeCommand) Invite(client auth.ClientI) error {
	// parse --roles flag
	roles, err := teleport.ParseRoles(c.roles)
	if err != nil {
		return trace.Wrap(err)
	}
	token, err := client.GenerateToken(auth.GenerateTokenRequest{Roles: roles, TTL: c.ttl, Token: c.token})
	if err != nil {
		return trace.Wrap(err)
	}

	authServers, err := client.GetAuthServers()
	if err != nil {
		return trace.Wrap(err)
	}
	if len(authServers) == 0 {
		return trace.Errorf("This cluster does not have any auth servers running.")
	}

	clusterName, err := client.GetClusterName()
	if err != nil {
		return trace.Wrap(err)
	}

	// output format swtich:
	if c.format == "text" {
		if roles.Include(teleport.RoleTrustedCluster) {
			trustedCluster := fmt.Sprintf(trustedClusterTemplate, clusterName.GetClusterName(), token)
			fmt.Printf(trustedClusterMessage, token, trustedCluster, int(c.ttl.Minutes()))
		} else {
			fmt.Printf(nodeMessage,
				token, strings.ToLower(roles.String()), token, authServers[0].GetAddr(), int(c.ttl.Minutes()), authServers[0].GetAddr())
		}
	} else {
		out, err := json.Marshal(token)
		if err != nil {
			return trace.Wrap(err, "failed to marshal token")
		}
		fmt.Printf(string(out))
	}
	return nil
}

// ListActive retreives the list of nodes who recently sent heartbeats to
// to a cluster and prints it to stdout
func (c *NodeCommand) ListActive(client auth.ClientI) error {
	nodes, err := client.GetNodes(c.namespace)
	if err != nil {
		return trace.Wrap(err)
	}
	coll := &serverCollection{servers: nodes}
	coll.writeText(os.Stdout)
	return nil
}
<commit_msg>Always return a list of tokens from "tctl nodes add --format=json ...".<commit_after>/*
Copyright 2015 Gravitational, Inc.

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

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

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

package common

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

	"github.com/gravitational/kingpin"
	"github.com/gravitational/teleport"
	"github.com/gravitational/teleport/lib/auth"
	"github.com/gravitational/teleport/lib/defaults"
	"github.com/gravitational/teleport/lib/service"
	"github.com/gravitational/trace"
)

// NodeCommand implements `tctl nodes` group of commands
type NodeCommand struct {
	config *service.Config
	// format is the output format, e.g. text or json
	format string
	// list of roles for the new node to assume
	roles string
	// TTL: duration of time during which a generated node token will
	// be valid.
	ttl time.Duration
	// namespace is node namespace
	namespace string
	// token is an optional custom token supplied by client,
	// if not specified, is autogenerated
	token string

	// CLI subcommands (clauses)
	nodeAdd  *kingpin.CmdClause
	nodeList *kingpin.CmdClause
}

// Initialize allows NodeCommand to plug itself into the CLI parser
func (c *NodeCommand) Initialize(app *kingpin.Application, config *service.Config) {
	c.config = config

	// add node command
	nodes := app.Command("nodes", "Issue invites for other nodes to join the cluster")
	c.nodeAdd = nodes.Command("add", "Generate a node invitation token")
	c.nodeAdd.Flag("roles", "Comma-separated list of roles for the new node to assume [node]").Default("node").StringVar(&c.roles)
	c.nodeAdd.Flag("ttl", "Time to live for a generated token").Default(defaults.ProvisioningTokenTTL.String()).DurationVar(&c.ttl)
	c.nodeAdd.Flag("token", "Custom token to use, autogenerated if not provided").StringVar(&c.token)
	c.nodeAdd.Flag("format", "Output format, 'text' or 'json'").Hidden().Default("text").StringVar(&c.format)
	c.nodeAdd.Alias(AddNodeHelp)

	c.nodeList = nodes.Command("ls", "List all active SSH nodes within the cluster")
	c.nodeList.Flag("namespace", "Namespace of the nodes").Default(defaults.Namespace).StringVar(&c.namespace)
	c.nodeList.Alias(ListNodesHelp)
}

// TryRun takes the CLI command as an argument (like "nodes ls") and executes it.
func (c *NodeCommand) TryRun(cmd string, client auth.ClientI) (match bool, err error) {
	switch cmd {
	case c.nodeAdd.FullCommand():
		err = c.Invite(client)
	case c.nodeList.FullCommand():
		err = c.ListActive(client)

	default:
		return false, nil
	}
	return true, trace.Wrap(err)
}

const trustedClusterTemplate = `kind: trusted_cluster
version: v2
metadata:
   name: %v
spec:
   enabled: true
   token: %v
   web_proxy_addr: proxy.example.com:3080
   role_map:
   - remote: admin
     local: [admin]`

const trustedClusterMessage = `Trusted cluster token: %v

Use this cluster in trusted cluster resource, for example:

%v

Please note:

  - This token will expire in %d minutes.
  - Replace address proxy.example.com:3080 with externally accessible teleport proxy address.
  - Set proper local and remote role_map property.
`

const nodeMessage = `The invite token: %v

Run this on the new node to join the cluster:

> teleport start --roles=%s --token=%v --auth-server=%v

Please note:

  - This invitation token will expire in %d minutes
  - %v must be reachable from the new node, see --advertise-ip server flag
`

// Invite generates a token which can be used to add another SSH node
// to a cluster
func (c *NodeCommand) Invite(client auth.ClientI) error {
	// parse --roles flag
	roles, err := teleport.ParseRoles(c.roles)
	if err != nil {
		return trace.Wrap(err)
	}
	token, err := client.GenerateToken(auth.GenerateTokenRequest{Roles: roles, TTL: c.ttl, Token: c.token})
	if err != nil {
		return trace.Wrap(err)
	}

	authServers, err := client.GetAuthServers()
	if err != nil {
		return trace.Wrap(err)
	}
	if len(authServers) == 0 {
		return trace.Errorf("This cluster does not have any auth servers running.")
	}

	clusterName, err := client.GetClusterName()
	if err != nil {
		return trace.Wrap(err)
	}

	// output format swtich:
	if c.format == "text" {
		if roles.Include(teleport.RoleTrustedCluster) {
			trustedCluster := fmt.Sprintf(trustedClusterTemplate, clusterName.GetClusterName(), token)
			fmt.Printf(trustedClusterMessage, token, trustedCluster, int(c.ttl.Minutes()))
		} else {
			fmt.Printf(nodeMessage,
				token, strings.ToLower(roles.String()), token, authServers[0].GetAddr(), int(c.ttl.Minutes()), authServers[0].GetAddr())
		}
	} else {
		// Always return a list, otherwise we'll break users tooling. See #1846 for
		// more details.
		tokens := []string{token}
		out, err := json.Marshal(tokens)
		if err != nil {
			return trace.Wrap(err, "failed to marshal token")
		}
		fmt.Printf(string(out))
	}
	return nil
}

// ListActive retreives the list of nodes who recently sent heartbeats to
// to a cluster and prints it to stdout
func (c *NodeCommand) ListActive(client auth.ClientI) error {
	nodes, err := client.GetNodes(c.namespace)
	if err != nil {
		return trace.Wrap(err)
	}
	coll := &serverCollection{servers: nodes}
	coll.writeText(os.Stdout)
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes Authors.

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

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

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

package cache

import (
	"fmt"
	"sync"

	"k8s.io/apimachinery/pkg/util/sets"
)

// ThreadSafeStore is an interface that allows concurrent indexed
// access to a storage backend.  It is like Indexer but does not
// (necessarily) know how to extract the Store key from a given
// object.
//
// TL;DR caveats: you must not modify anything returned by Get or List as it will break
// the indexing feature in addition to not being thread safe.
//
// The guarantees of thread safety provided by List/Get are only valid if the caller
// treats returned items as read-only. For example, a pointer inserted in the store
// through `Add` will be returned as is by `Get`. Multiple clients might invoke `Get`
// on the same key and modify the pointer in a non-thread-safe way. Also note that
// modifying objects stored by the indexers (if any) will *not* automatically lead
// to a re-index. So it's not a good idea to directly modify the objects returned by
// Get/List, in general.
type ThreadSafeStore interface {
	Add(key string, obj interface{})
	Update(key string, obj interface{})
	Delete(key string)
	Get(key string) (item interface{}, exists bool)
	List() []interface{}
	ListKeys() []string
	Replace(map[string]interface{}, string)
	Index(indexName string, obj interface{}) ([]interface{}, error)
	IndexKeys(indexName, indexKey string) ([]string, error)
	ListIndexFuncValues(name string) []string
	ByIndex(indexName, indexKey string) ([]interface{}, error)
	GetIndexers() Indexers

	// AddIndexers adds more indexers to this store.  If you call this after you already have data
	// in the store, the results are undefined.
	AddIndexers(newIndexers Indexers) error
	// Resync is a no-op and is deprecated
	Resync() error
}

// threadSafeMap implements ThreadSafeStore
type threadSafeMap struct {
	lock  sync.RWMutex
	items map[string]interface{}

	// indexers maps a name to an IndexFunc
	indexers Indexers
	// indices maps a name to an Index
	indices Indices
}

func (c *threadSafeMap) Add(key string, obj interface{}) {
	c.Update(key, obj)
}

func (c *threadSafeMap) Update(key string, obj interface{}) {
	c.lock.Lock()
	defer c.lock.Unlock()
	oldObject := c.items[key]
	c.items[key] = obj
	c.updateIndices(oldObject, obj, key)
}

func (c *threadSafeMap) Delete(key string) {
	c.lock.Lock()
	defer c.lock.Unlock()
	if obj, exists := c.items[key]; exists {
		c.updateIndices(obj, nil, key)
		delete(c.items, key)
	}
}

func (c *threadSafeMap) Get(key string) (item interface{}, exists bool) {
	c.lock.RLock()
	defer c.lock.RUnlock()
	item, exists = c.items[key]
	return item, exists
}

func (c *threadSafeMap) List() []interface{} {
	c.lock.RLock()
	defer c.lock.RUnlock()
	list := make([]interface{}, 0, len(c.items))
	for _, item := range c.items {
		list = append(list, item)
	}
	return list
}

// ListKeys returns a list of all the keys of the objects currently
// in the threadSafeMap.
func (c *threadSafeMap) ListKeys() []string {
	c.lock.RLock()
	defer c.lock.RUnlock()
	list := make([]string, 0, len(c.items))
	for key := range c.items {
		list = append(list, key)
	}
	return list
}

func (c *threadSafeMap) Replace(items map[string]interface{}, resourceVersion string) {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.items = items

	// rebuild any index
	c.indices = Indices{}
	for key, item := range c.items {
		c.updateIndices(nil, item, key)
	}
}

// Index returns a list of items that match the given object on the index function.
// Index is thread-safe so long as you treat all items as immutable.
func (c *threadSafeMap) Index(indexName string, obj interface{}) ([]interface{}, error) {
	c.lock.RLock()
	defer c.lock.RUnlock()

	indexFunc := c.indexers[indexName]
	if indexFunc == nil {
		return nil, fmt.Errorf("Index with name %s does not exist", indexName)
	}

	indexedValues, err := indexFunc(obj)
	if err != nil {
		return nil, err
	}
	index := c.indices[indexName]

	var storeKeySet sets.String
	if len(indexedValues) == 1 {
		// In majority of cases, there is exactly one value matching.
		// Optimize the most common path - deduping is not needed here.
		storeKeySet = index[indexedValues[0]]
	} else {
		// Need to de-dupe the return list.
		// Since multiple keys are allowed, this can happen.
		storeKeySet = sets.String{}
		for _, indexedValue := range indexedValues {
			for key := range index[indexedValue] {
				storeKeySet.Insert(key)
			}
		}
	}

	list := make([]interface{}, 0, storeKeySet.Len())
	for storeKey := range storeKeySet {
		list = append(list, c.items[storeKey])
	}
	return list, nil
}

// ByIndex returns a list of the items whose indexed values in the given index include the given indexed value
func (c *threadSafeMap) ByIndex(indexName, indexedValue string) ([]interface{}, error) {
	c.lock.RLock()
	defer c.lock.RUnlock()

	indexFunc := c.indexers[indexName]
	if indexFunc == nil {
		return nil, fmt.Errorf("Index with name %s does not exist", indexName)
	}

	index := c.indices[indexName]

	set := index[indexedValue]
	list := make([]interface{}, 0, set.Len())
	for key := range set {
		list = append(list, c.items[key])
	}

	return list, nil
}

// IndexKeys returns a list of the Store keys of the objects whose indexed values in the given index include the given indexed value.
// IndexKeys is thread-safe so long as you treat all items as immutable.
func (c *threadSafeMap) IndexKeys(indexName, indexedValue string) ([]string, error) {
	c.lock.RLock()
	defer c.lock.RUnlock()

	indexFunc := c.indexers[indexName]
	if indexFunc == nil {
		return nil, fmt.Errorf("Index with name %s does not exist", indexName)
	}

	index := c.indices[indexName]

	set := index[indexedValue]
	return set.List(), nil
}

func (c *threadSafeMap) ListIndexFuncValues(indexName string) []string {
	c.lock.RLock()
	defer c.lock.RUnlock()

	index := c.indices[indexName]
	names := make([]string, 0, len(index))
	for key := range index {
		names = append(names, key)
	}
	return names
}

func (c *threadSafeMap) GetIndexers() Indexers {
	return c.indexers
}

func (c *threadSafeMap) AddIndexers(newIndexers Indexers) error {
	c.lock.Lock()
	defer c.lock.Unlock()

	if len(c.items) > 0 {
		return fmt.Errorf("cannot add indexers to running index")
	}

	oldKeys := sets.StringKeySet(c.indexers)
	newKeys := sets.StringKeySet(newIndexers)

	if oldKeys.HasAny(newKeys.List()...) {
		return fmt.Errorf("indexer conflict: %v", oldKeys.Intersection(newKeys))
	}

	for k, v := range newIndexers {
		c.indexers[k] = v
	}
	return nil
}

// updateIndices modifies the objects location in the managed indexes:
// - for create you must provide only the newObj
// - for update you must provide both the oldObj and the newObj
// - for delete you must provide only the oldObj
// updateIndices must be called from a function that already has a lock on the cache
func (c *threadSafeMap) updateIndices(oldObj interface{}, newObj interface{}, key string) {
	var oldIndexValues, indexValues []string
	var err error
	for name, indexFunc := range c.indexers {
		if oldObj != nil {
			oldIndexValues, err = indexFunc(oldObj)
		} else {
			oldIndexValues = oldIndexValues[:0]
		}
		if err != nil {
			panic(fmt.Errorf("unable to calculate an index entry for key %q on index %q: %v", key, name, err))
		}

		if newObj != nil {
			indexValues, err = indexFunc(newObj)
		} else {
			indexValues = indexValues[:0]
		}
		if err != nil {
			panic(fmt.Errorf("unable to calculate an index entry for key %q on index %q: %v", key, name, err))
		}

		index := c.indices[name]
		if index == nil {
			index = Index{}
			c.indices[name] = index
		}

		for _, value := range oldIndexValues {
			// We optimize for the most common case where indexFunc returns a single value.
			if len(indexValues) == 1 && value == indexValues[0] {
				continue
			}
			c.deleteKeyFromIndex(key, value, index)
		}
		for _, value := range indexValues {
			// We optimize for the most common case where indexFunc returns a single value.
			if len(oldIndexValues) == 1 && value == oldIndexValues[0] {
				continue
			}
			c.addKeyToIndex(key, value, index)
		}
	}
}

func (c *threadSafeMap) addKeyToIndex(key, indexValue string, index Index) {
	set := index[indexValue]
	if set == nil {
		set = sets.String{}
		index[indexValue] = set
	}
	set.Insert(key)
}

func (c *threadSafeMap) deleteKeyFromIndex(key, indexValue string, index Index) {
	set := index[indexValue]
	if set == nil {
		return
	}
	set.Delete(key)
	// If we don't delete the set when zero, indices with high cardinality
	// short lived resources can cause memory to increase over time from
	// unused empty sets. See `kubernetes/kubernetes/issues/84959`.
	if len(set) == 0 {
		delete(index, indexValue)
	}
}

func (c *threadSafeMap) Resync() error {
	// Nothing to do
	return nil
}

// NewThreadSafeStore creates a new instance of ThreadSafeStore.
func NewThreadSafeStore(indexers Indexers, indices Indices) ThreadSafeStore {
	return &threadSafeMap{
		items:    map[string]interface{}{},
		indexers: indexers,
		indices:  indices,
	}
}
<commit_msg>Addresses the issue which caused #109115<commit_after>/*
Copyright 2014 The Kubernetes Authors.

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

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

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

package cache

import (
	"fmt"
	"sync"

	"k8s.io/apimachinery/pkg/util/sets"
)

// ThreadSafeStore is an interface that allows concurrent indexed
// access to a storage backend.  It is like Indexer but does not
// (necessarily) know how to extract the Store key from a given
// object.
//
// TL;DR caveats: you must not modify anything returned by Get or List as it will break
// the indexing feature in addition to not being thread safe.
//
// The guarantees of thread safety provided by List/Get are only valid if the caller
// treats returned items as read-only. For example, a pointer inserted in the store
// through `Add` will be returned as is by `Get`. Multiple clients might invoke `Get`
// on the same key and modify the pointer in a non-thread-safe way. Also note that
// modifying objects stored by the indexers (if any) will *not* automatically lead
// to a re-index. So it's not a good idea to directly modify the objects returned by
// Get/List, in general.
type ThreadSafeStore interface {
	Add(key string, obj interface{})
	Update(key string, obj interface{})
	Delete(key string)
	Get(key string) (item interface{}, exists bool)
	List() []interface{}
	ListKeys() []string
	Replace(map[string]interface{}, string)
	Index(indexName string, obj interface{}) ([]interface{}, error)
	IndexKeys(indexName, indexKey string) ([]string, error)
	ListIndexFuncValues(name string) []string
	ByIndex(indexName, indexKey string) ([]interface{}, error)
	GetIndexers() Indexers

	// AddIndexers adds more indexers to this store.  If you call this after you already have data
	// in the store, the results are undefined.
	AddIndexers(newIndexers Indexers) error
	// Resync is a no-op and is deprecated
	Resync() error
}

// threadSafeMap implements ThreadSafeStore
type threadSafeMap struct {
	lock  sync.RWMutex
	items map[string]interface{}

	// indexers maps a name to an IndexFunc
	indexers Indexers
	// indices maps a name to an Index
	indices Indices
}

func (c *threadSafeMap) Add(key string, obj interface{}) {
	c.Update(key, obj)
}

func (c *threadSafeMap) Update(key string, obj interface{}) {
	c.lock.Lock()
	defer c.lock.Unlock()
	oldObject := c.items[key]
	c.items[key] = obj
	c.updateIndices(oldObject, obj, key)
}

func (c *threadSafeMap) Delete(key string) {
	c.lock.Lock()
	defer c.lock.Unlock()
	if obj, exists := c.items[key]; exists {
		c.updateIndices(obj, nil, key)
		delete(c.items, key)
	}
}

func (c *threadSafeMap) Get(key string) (item interface{}, exists bool) {
	c.lock.RLock()
	defer c.lock.RUnlock()
	item, exists = c.items[key]
	return item, exists
}

func (c *threadSafeMap) List() []interface{} {
	c.lock.RLock()
	defer c.lock.RUnlock()
	list := make([]interface{}, 0, len(c.items))
	for _, item := range c.items {
		list = append(list, item)
	}
	return list
}

// ListKeys returns a list of all the keys of the objects currently
// in the threadSafeMap.
func (c *threadSafeMap) ListKeys() []string {
	c.lock.RLock()
	defer c.lock.RUnlock()
	list := make([]string, 0, len(c.items))
	for key := range c.items {
		list = append(list, key)
	}
	return list
}

func (c *threadSafeMap) Replace(items map[string]interface{}, resourceVersion string) {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.items = items

	// rebuild any index
	c.indices = Indices{}
	for key, item := range c.items {
		c.updateIndices(nil, item, key)
	}
}

// Index returns a list of items that match the given object on the index function.
// Index is thread-safe so long as you treat all items as immutable.
func (c *threadSafeMap) Index(indexName string, obj interface{}) ([]interface{}, error) {
	c.lock.RLock()
	defer c.lock.RUnlock()

	indexFunc := c.indexers[indexName]
	if indexFunc == nil {
		return nil, fmt.Errorf("Index with name %s does not exist", indexName)
	}

	indexedValues, err := indexFunc(obj)
	if err != nil {
		return nil, err
	}
	index := c.indices[indexName]

	var storeKeySet sets.String
	if len(indexedValues) == 1 {
		// In majority of cases, there is exactly one value matching.
		// Optimize the most common path - deduping is not needed here.
		storeKeySet = index[indexedValues[0]]
	} else {
		// Need to de-dupe the return list.
		// Since multiple keys are allowed, this can happen.
		storeKeySet = sets.String{}
		for _, indexedValue := range indexedValues {
			for key := range index[indexedValue] {
				storeKeySet.Insert(key)
			}
		}
	}

	list := make([]interface{}, 0, storeKeySet.Len())
	for storeKey := range storeKeySet {
		list = append(list, c.items[storeKey])
	}
	return list, nil
}

// ByIndex returns a list of the items whose indexed values in the given index include the given indexed value
func (c *threadSafeMap) ByIndex(indexName, indexedValue string) ([]interface{}, error) {
	c.lock.RLock()
	defer c.lock.RUnlock()

	indexFunc := c.indexers[indexName]
	if indexFunc == nil {
		return nil, fmt.Errorf("Index with name %s does not exist", indexName)
	}

	index := c.indices[indexName]

	set := index[indexedValue]
	list := make([]interface{}, 0, set.Len())
	for key := range set {
		list = append(list, c.items[key])
	}

	return list, nil
}

// IndexKeys returns a list of the Store keys of the objects whose indexed values in the given index include the given indexed value.
// IndexKeys is thread-safe so long as you treat all items as immutable.
func (c *threadSafeMap) IndexKeys(indexName, indexedValue string) ([]string, error) {
	c.lock.RLock()
	defer c.lock.RUnlock()

	indexFunc := c.indexers[indexName]
	if indexFunc == nil {
		return nil, fmt.Errorf("Index with name %s does not exist", indexName)
	}

	index := c.indices[indexName]

	set := index[indexedValue]
	return set.List(), nil
}

func (c *threadSafeMap) ListIndexFuncValues(indexName string) []string {
	c.lock.RLock()
	defer c.lock.RUnlock()

	index := c.indices[indexName]
	names := make([]string, 0, len(index))
	for key := range index {
		names = append(names, key)
	}
	return names
}

func (c *threadSafeMap) GetIndexers() Indexers {
	return c.indexers
}

func (c *threadSafeMap) AddIndexers(newIndexers Indexers) error {
	c.lock.Lock()
	defer c.lock.Unlock()

	if len(c.items) > 0 {
		return fmt.Errorf("cannot add indexers to running index")
	}

	oldKeys := sets.StringKeySet(c.indexers)
	newKeys := sets.StringKeySet(newIndexers)

	if oldKeys.HasAny(newKeys.List()...) {
		return fmt.Errorf("indexer conflict: %v", oldKeys.Intersection(newKeys))
	}

	for k, v := range newIndexers {
		c.indexers[k] = v
	}
	return nil
}

// updateIndices modifies the objects location in the managed indexes:
// - for create you must provide only the newObj
// - for update you must provide both the oldObj and the newObj
// - for delete you must provide only the oldObj
// updateIndices must be called from a function that already has a lock on the cache
func (c *threadSafeMap) updateIndices(oldObj interface{}, newObj interface{}, key string) {
	var oldIndexValues, indexValues []string
	var err error
	for name, indexFunc := range c.indexers {
		if oldObj != nil {
			oldIndexValues, err = indexFunc(oldObj)
		} else {
			oldIndexValues = oldIndexValues[:0]
		}
		if err != nil {
			panic(fmt.Errorf("unable to calculate an index entry for key %q on index %q: %v", key, name, err))
		}

		if newObj != nil {
			indexValues, err = indexFunc(newObj)
		} else {
			indexValues = indexValues[:0]
		}
		if err != nil {
			panic(fmt.Errorf("unable to calculate an index entry for key %q on index %q: %v", key, name, err))
		}

		index := c.indices[name]
		if index == nil {
			index = Index{}
			c.indices[name] = index
		}

		if len(indexValues) == 1 && len(oldIndexValues) == 1 && indexValues[0] == oldIndexValues[0] {
			// We optimize for the most common case where indexFunc returns a single value which has not been changed
			continue
		}

		for _, value := range oldIndexValues {
			c.deleteKeyFromIndex(key, value, index)
		}
		for _, value := range indexValues {
			c.addKeyToIndex(key, value, index)
		}
	}
}

func (c *threadSafeMap) addKeyToIndex(key, indexValue string, index Index) {
	set := index[indexValue]
	if set == nil {
		set = sets.String{}
		index[indexValue] = set
	}
	set.Insert(key)
}

func (c *threadSafeMap) deleteKeyFromIndex(key, indexValue string, index Index) {
	set := index[indexValue]
	if set == nil {
		return
	}
	set.Delete(key)
	// If we don't delete the set when zero, indices with high cardinality
	// short lived resources can cause memory to increase over time from
	// unused empty sets. See `kubernetes/kubernetes/issues/84959`.
	if len(set) == 0 {
		delete(index, indexValue)
	}
}

func (c *threadSafeMap) Resync() error {
	// Nothing to do
	return nil
}

// NewThreadSafeStore creates a new instance of ThreadSafeStore.
func NewThreadSafeStore(indexers Indexers, indices Indices) ThreadSafeStore {
	return &threadSafeMap{
		items:    map[string]interface{}{},
		indexers: indexers,
		indices:  indices,
	}
}
<|endoftext|>


<commit_before>package gen

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"

	"github.com/gocraft/health"
	"github.com/robertkrimen/otto"
	"github.com/robertkrimen/otto/underscore"
	"github.com/spf13/afero"
)

//package specfic halth stream. This can be used to provide consistent
//application wise health check stream.
//
// It is safe for concurrent usage.
var stream *health.Stream

func init() {
	stream = health.NewStream()
}

const (
	scriptsDir = "scripts"
	initDir    = "init"
	planDir    = "plan"
	configFile = "config.json"
	indexFile  = "index.js"
)

//Initilize initializes the build process. Any stages after this will have the generator
//already bootstraped.
//
// It is possible to bootstrap the generator from the project( User's side) by
// providing an entry javascript file in the default path of
// scripts/init/index.js which will be executed and you can overide the default
// entry script which is evaluated internally
//
// Initialzation is offloaded to the javascript runtine of the generator..Any
// error returned is a build error.
func Initilize(ctx *Context) error {
	_ = ctx.VM.Set("sys", func(call otto.FunctionCall) otto.Value {
		data, err := json.Marshal(ctx.Sys)
		if err != nil {
			panicOtto(err)
		}
		val, err := call.Otto.Call("JSON.parse", nil, string(data))
		if err != nil {
			panicOtto(err)
		}
		return val
	})
	_, err := ctx.VM.Eval(entryScript())
	if err != nil {
		return err
	}

	_ = ctx.VM.Set("fileTree", fileTree(ctx.FS, ctx.WorkDir))

	// evaluate project provided entry script if provided. We ignore if the file
	// is not provided but any errors arsing from evaluating a provided script is
	// a built error.
	entryFile := filepath.Join(scriptsDir, initDir, indexFile)
	err = EvaluateFile(ctx.FS, ctx.VM, entryFile)
	if err != nil {
		if !os.IsNotExist(err) {
			return err
		}
	}
	return nil
}

//fileTree provides an array of all files found in the root( which is suppose to
//be the working directory.
//
//The file index is built only once and evaluated once too, then it is cached.
//This operates in the otto runtime.
func fileTree(fs afero.Fs, root string) func(otto.FunctionCall) otto.Value {
	var tree []string
	var v otto.Value
	return func(call otto.FunctionCall) otto.Value {
		if tree != nil {
			return v
		}
		ferr := afero.Walk(fs, "/", func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}
			if !info.IsDir() {
				tree = append(tree, path)
			}
			return nil
		})
		if ferr != nil {
			tree = nil
			panicOtto(ferr)
		}
		v, _ = call.Otto.ToValue(tree)
		return v
	}
}

func defaultSystem() *System {
	return &System{
		Boot: &Boot{
			ConfigiFile: configFile,
			PlanFile:    "index.js",
		},
	}
}

//EvaluateFile  opens the file in the specified path and evaluates it withing the context of
// the javascript runtine.
//
// The evaluated javascript code can mutate the global state. Use execFile to
// execute the javascript without mutating the state of the generato'r
// javascript runtime.
func EvaluateFile(fs afero.Fs, vm *otto.Otto, path string) error {
	f, err := fs.Open(path)
	if err != nil {
		return err
	}
	defer func() { _ = f.Close() }()
	data, err := ioutil.ReadAll(f)
	if err != nil {
		return err
	}
	_, err = vm.Eval(data)
	if err != nil {
		return err
	}
	return nil
}

func defaultVM(sys *System) *otto.Otto {
	return otto.New()
}

//Configure configures the ctx with default values. This make sure the ctx is in
//good shape to be passed around to any other stages.
//
// It is safe to pass an eunintialized *Context, when you want to expect default
// behaviour.
//
// passing unitialized *Context will make the current working directory as the
// context WOrkDir. This can have unintended effect so be warned and just make
// sure the context passed has WorkDir set if you inted to do something
// otherwise.
func Configure(ctx *Context) error {
	if ctx.WorkDir == "" {

		// we assing the current working directory as the context WOrkDir. This
		// is ugly, and may result into bugs. But it is important for a WorkDir
		// to be set that is why this is left here.
		wd, err := os.Getwd()
		if err != nil {
			return err
		}
		ctx.WorkDir = wd
	}
	if ctx.Out == nil {
		ctx.Out = os.Stdout
	}
	if ctx.Job == nil {
		if stream.Sinks == nil {
			stream.AddSink(&health.WriterSink{Writer: ctx.Out})
		}
		ctx.Job = stream.NewJob("generate:" + ctx.WorkDir)
	}
	if ctx.FS == nil {
		ctx.FS = afero.NewOsFs()
	}
	if ctx.Sys == nil {
		ctx.Sys = defaultSystem()
	}
	if ctx.VM == nil {
		ctx.VM = defaultVM(ctx.Sys)
	}

	// ensure everything is relative to the working directory
	// The working directory is where the  directory in which the project to be
	// built lives.
	//
	// All file operations, happens within the diretory. So to access the
	// configuration file for example it is located in /config.json.
	ctx.FS = afero.NewBasePathFs(ctx.FS, ctx.WorkDir)
	af := afero.Afero{Fs: ctx.FS}
	data, err := af.ReadFile(configFile)
	if err != nil {
		return err
	}
	cfg := &Config{}
	err = json.Unmarshal(data, cfg)
	if err != nil {
		return err
	}
	ctx.Sys.Config = cfg
	return AddRequire(ctx)
}

//AddRequire adds require function to the ctx VM. It also loads the modules that
//are shipped with the package( a.k.a builtin modules).
func AddRequire(ctx *Context) error {
	// Add reuire
	req := NewRequire(ctx.FS, scriptsDir)
	err := RegisterBuiltins(ctx, req)
	if err != nil {
		return err
	}
	_ = ctx.VM.Set("require", req.Load)
	return nil
}

//PlanExecution is the planning phase of the generator. First a project specific plan
//file is evaluated, the file is loacetd in /scripts_dir/plan_fir/index.js for
//example /scripts/plan/index.js. Second the existing system plan is merged with
//default plan.
//
//TODO: Use user defined plan only when it is set, and use default plan only
//when there is no user defined plan.
//
// This is executed to prepare the Plan object, which is the blueprint on how
// the whole execution is going to take place.
func PlanExecution(ctx *Context) error {
	pFile := filepath.Join(scriptsDir, planDir, indexFile)
	err := EvaluateFile(ctx.FS, ctx.VM, pFile)
	if err != nil {
		if !os.IsNotExist(err) {
			return err
		}
	}
	v, err := ctx.VM.Call("getCurrentSys", nil)
	if err != nil {
		return err
	}
	str, err := v.ToString()
	if err != nil {
		return err
	}
	sys := &System{}
	err = json.Unmarshal([]byte(str), sys)
	if err != nil {
		return err
	}
	if ctx.Sys.Plan == nil && sys.Plan != nil {
		ctx.Sys.Plan = sys.Plan
	}
	if ctx.Sys.Plan == nil && sys.Plan == nil {
		ctx.Sys.Plan = defaultPlan()
	}
	o, err := ctx.VM.Call("prepare", nil, ctx.Sys.Plan)
	if err != nil {
		return err
	}
	ok, err := o.ToBoolean()
	if err != nil {
		return err
	}
	if !ok {
		return errors.New("trouble preparing plan")
	}
	return nil
}

//Execute  executes the plans that are outilend by the plans found in ctx.
//
// Execution is done in two phases. First the files are evaluated using the
// matching plans. Plans are per file. Secondly the rendering of the site is
// done.
//
// Evaluation of files is done concurrently, then the results are aggregated
// back together.
//
// Rendering is done synchronously. As there is a lot of cross refences and
// stuffs to be considered, the rendering function is provided by the plan.
func Execute(ctx *Context) error {
	o, err := ctx.VM.Call("fileTree", nil)
	if err != nil {
		return err
	}
	ov, err := o.Export()
	if err != nil {
		return err
	}
	if ov == nil {
		// No files to mess with
		return errors.New("no files to build")
	}
	files, ok := ov.([]string)
	if !ok {
		// Some fish
		return err
	}

	var wg sync.WaitGroup
	var list FileList
	var errlist []error
	p := ctx.Sys.Plan
	for i := range files {
		wg.Add(1)
		go func(fn string) {
			defer wg.Done()
			s, err := p.FindStrategy(fn)
			if err != nil {
				errlist = append(errlist, err)
				return
			}
			f, err := ExecStrategy(ctx, fn, s)
			if err != nil {
				errlist = append(errlist, err)
				return
			}
			list = append(list, f)
		}(files[i])

	}
	wg.Wait()
	return ExecPlan(ctx, list, p)
}

//ExecStrategy executes the stratedy using the given ctx. The endgame is a *File
//as we are only interested in processing the file  named filename.
//
// The strategy outlines how filename is processed.
func ExecStrategy(ctx *Context, filename string, s *Strategy) (*File, error) {
	return nil, nil
}

//ExecPlan executes plan s for processing the FileList fi using the conetx ctx.
func ExecPlan(ctx *Context, fl FileList, s *Plan) error {
	return nil
}

//RegisterBuiltins adds important modules  like underscore and fs.
func RegisterBuiltins(ctx *Context, r *Require) error {
	f := &fileSys{}
	f.Fs = ctx.FS
	v := f.export().ToValue(ctx.VM)
	r.addToCache("fs", v)
	r.addToCache("markdown", markdown().ToValue(ctx.VM))
	_, err := ctx.VM.Run(underscore.Source())
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>gen: Add plugin dir to require path<commit_after>package gen

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"

	"github.com/gocraft/health"
	"github.com/robertkrimen/otto"
	"github.com/robertkrimen/otto/underscore"
	"github.com/spf13/afero"
)

//package specfic halth stream. This can be used to provide consistent
//application wise health check stream.
//
// It is safe for concurrent usage.
var stream *health.Stream

func init() {
	stream = health.NewStream()
}

const (
	scriptsDir = "scripts"
	initDir    = "init"
	planDir    = "plan"
	configFile = "config.json"
	indexFile  = "index.js"
)

//Initilize initializes the build process. Any stages after this will have the generator
//already bootstraped.
//
// It is possible to bootstrap the generator from the project( User's side) by
// providing an entry javascript file in the default path of
// scripts/init/index.js which will be executed and you can overide the default
// entry script which is evaluated internally
//
// Initialzation is offloaded to the javascript runtine of the generator..Any
// error returned is a build error.
func Initilize(ctx *Context) error {
	_ = ctx.VM.Set("sys", func(call otto.FunctionCall) otto.Value {
		data, err := json.Marshal(ctx.Sys)
		if err != nil {
			panicOtto(err)
		}
		val, err := call.Otto.Call("JSON.parse", nil, string(data))
		if err != nil {
			panicOtto(err)
		}
		return val
	})
	_, err := ctx.VM.Eval(entryScript())
	if err != nil {
		return err
	}

	_ = ctx.VM.Set("fileTree", fileTree(ctx.FS, ctx.WorkDir))

	// evaluate project provided entry script if provided. We ignore if the file
	// is not provided but any errors arsing from evaluating a provided script is
	// a built error.
	entryFile := filepath.Join(scriptsDir, initDir, indexFile)
	err = EvaluateFile(ctx.FS, ctx.VM, entryFile)
	if err != nil {
		if !os.IsNotExist(err) {
			return err
		}
	}
	return nil
}

//fileTree provides an array of all files found in the root( which is suppose to
//be the working directory.
//
//The file index is built only once and evaluated once too, then it is cached.
//This operates in the otto runtime.
func fileTree(fs afero.Fs, root string) func(otto.FunctionCall) otto.Value {
	var tree []string
	var v otto.Value
	return func(call otto.FunctionCall) otto.Value {
		if tree != nil {
			return v
		}
		ferr := afero.Walk(fs, "/", func(path string, info os.FileInfo, err error) error {
			if err != nil {
				return err
			}
			if !info.IsDir() {
				tree = append(tree, path)
			}
			return nil
		})
		if ferr != nil {
			tree = nil
			panicOtto(ferr)
		}
		v, _ = call.Otto.ToValue(tree)
		return v
	}
}

func defaultSystem() *System {
	return &System{
		Boot: &Boot{
			ConfigiFile: configFile,
			PlanFile:    "index.js",
		},
	}
}

//EvaluateFile  opens the file in the specified path and evaluates it withing the context of
// the javascript runtine.
//
// The evaluated javascript code can mutate the global state. Use execFile to
// execute the javascript without mutating the state of the generato'r
// javascript runtime.
func EvaluateFile(fs afero.Fs, vm *otto.Otto, path string) error {
	f, err := fs.Open(path)
	if err != nil {
		return err
	}
	defer func() { _ = f.Close() }()
	data, err := ioutil.ReadAll(f)
	if err != nil {
		return err
	}
	_, err = vm.Eval(data)
	if err != nil {
		return err
	}
	return nil
}

func defaultVM(sys *System) *otto.Otto {
	return otto.New()
}

//Configure configures the ctx with default values. This make sure the ctx is in
//good shape to be passed around to any other stages.
//
// It is safe to pass an eunintialized *Context, when you want to expect default
// behaviour.
//
// passing unitialized *Context will make the current working directory as the
// context WOrkDir. This can have unintended effect so be warned and just make
// sure the context passed has WorkDir set if you inted to do something
// otherwise.
func Configure(ctx *Context) error {
	if ctx.WorkDir == "" {

		// we assing the current working directory as the context WOrkDir. This
		// is ugly, and may result into bugs. But it is important for a WorkDir
		// to be set that is why this is left here.
		wd, err := os.Getwd()
		if err != nil {
			return err
		}
		ctx.WorkDir = wd
	}
	if ctx.Out == nil {
		ctx.Out = os.Stdout
	}
	if ctx.Job == nil {
		if stream.Sinks == nil {
			stream.AddSink(&health.WriterSink{Writer: ctx.Out})
		}
		ctx.Job = stream.NewJob("generate:" + ctx.WorkDir)
	}
	if ctx.FS == nil {
		ctx.FS = afero.NewOsFs()
	}
	if ctx.Sys == nil {
		ctx.Sys = defaultSystem()
	}
	if ctx.VM == nil {
		ctx.VM = defaultVM(ctx.Sys)
	}

	// ensure everything is relative to the working directory
	// The working directory is where the  directory in which the project to be
	// built lives.
	//
	// All file operations, happens within the diretory. So to access the
	// configuration file for example it is located in /config.json.
	ctx.FS = afero.NewBasePathFs(ctx.FS, ctx.WorkDir)
	af := afero.Afero{Fs: ctx.FS}
	data, err := af.ReadFile(configFile)
	if err != nil {
		return err
	}
	cfg := &Config{}
	err = json.Unmarshal(data, cfg)
	if err != nil {
		return err
	}
	ctx.Sys.Config = cfg
	return AddRequire(ctx)
}

//AddRequire adds require function to the ctx VM. It also loads the modules that
//are shipped with the package( a.k.a builtin modules).
func AddRequire(ctx *Context) error {
	// Add reuire
	req := NewRequire(ctx.FS, scriptsDir)
	if ctx.Sys.Config != nil {
		req.Paths = append(req.Paths, ctx.Sys.Config.PluginDir)
	}
	err := RegisterBuiltins(ctx, req)
	if err != nil {
		return err
	}
	_ = ctx.VM.Set("require", req.Load)
	return nil
}

//PlanExecution is the planning phase of the generator. First a project specific plan
//file is evaluated, the file is loacetd in /scripts_dir/plan_fir/index.js for
//example /scripts/plan/index.js. Second the existing system plan is merged with
//default plan.
//
//TODO: Use user defined plan only when it is set, and use default plan only
//when there is no user defined plan.
//
// This is executed to prepare the Plan object, which is the blueprint on how
// the whole execution is going to take place.
func PlanExecution(ctx *Context) error {
	pFile := filepath.Join(scriptsDir, planDir, indexFile)
	err := EvaluateFile(ctx.FS, ctx.VM, pFile)
	if err != nil {
		if !os.IsNotExist(err) {
			return err
		}
	}
	v, err := ctx.VM.Call("getCurrentSys", nil)
	if err != nil {
		return err
	}
	str, err := v.ToString()
	if err != nil {
		return err
	}
	sys := &System{}
	err = json.Unmarshal([]byte(str), sys)
	if err != nil {
		return err
	}
	if ctx.Sys.Plan == nil && sys.Plan != nil {
		ctx.Sys.Plan = sys.Plan
	}
	if ctx.Sys.Plan == nil && sys.Plan == nil {
		ctx.Sys.Plan = defaultPlan()
	}
	o, err := ctx.VM.Call("prepare", nil, ctx.Sys.Plan)
	if err != nil {
		return err
	}
	ok, err := o.ToBoolean()
	if err != nil {
		return err
	}
	if !ok {
		return errors.New("trouble preparing plan")
	}
	return nil
}

//Execute  executes the plans that are outilend by the plans found in ctx.
//
// Execution is done in two phases. First the files are evaluated using the
// matching plans. Plans are per file. Secondly the rendering of the site is
// done.
//
// Evaluation of files is done concurrently, then the results are aggregated
// back together.
//
// Rendering is done synchronously. As there is a lot of cross refences and
// stuffs to be considered, the rendering function is provided by the plan.
func Execute(ctx *Context) error {
	o, err := ctx.VM.Call("fileTree", nil)
	if err != nil {
		return err
	}
	ov, err := o.Export()
	if err != nil {
		return err
	}
	if ov == nil {
		// No files to mess with
		return errors.New("no files to build")
	}
	files, ok := ov.([]string)
	if !ok {
		// Some fish
		return err
	}

	var wg sync.WaitGroup
	var list FileList
	var errlist []error
	p := ctx.Sys.Plan
	for i := range files {
		wg.Add(1)
		go func(fn string) {
			defer wg.Done()
			s, err := p.FindStrategy(fn)
			if err != nil {
				errlist = append(errlist, err)
				return
			}
			f, err := ExecStrategy(ctx, fn, s)
			if err != nil {
				errlist = append(errlist, err)
				return
			}
			list = append(list, f)
		}(files[i])

	}
	wg.Wait()
	return ExecPlan(ctx, list, p)
}

//ExecStrategy executes the stratedy using the given ctx. The endgame is a *File
//as we are only interested in processing the file  named filename.
//
// The strategy outlines how filename is processed.
func ExecStrategy(ctx *Context, filename string, s *Strategy) (*File, error) {
	return nil, nil
}

//ExecPlan executes plan s for processing the FileList fi using the conetx ctx.
func ExecPlan(ctx *Context, fl FileList, s *Plan) error {
	return nil
}

//RegisterBuiltins adds important modules  like underscore and fs.
func RegisterBuiltins(ctx *Context, r *Require) error {
	f := &fileSys{}
	f.Fs = ctx.FS
	v := f.export().ToValue(ctx.VM)
	r.addToCache("fs", v)
	r.addToCache("markdown", markdown().ToValue(ctx.VM))
	_, err := ctx.VM.Run(underscore.Source())
	if err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package null

import (
	"errors"
	"fmt"

	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/environs/config"
	"launchpad.net/juju-core/provider"
	"launchpad.net/juju-core/utils"
)

type nullProvider struct{}

func init() {
	environs.RegisterProvider(provider.Null, nullProvider{})
}

var errNoBootstrapHost = errors.New("bootstrap-host must be specified")

func (p nullProvider) Prepare(cfg *config.Config) (environs.Environ, error) {
	return p.Open(cfg)
}

func (p nullProvider) Open(cfg *config.Config) (environs.Environ, error) {
	envConfig, err := p.validate(cfg, nil)
	if err != nil {
		return nil, err
	}
	return &nullEnviron{cfg: envConfig}, nil
}

func checkImmutableString(cfg, old *environConfig, key string) error {
	if old.attrs[key] != cfg.attrs[key] {
		return fmt.Errorf("cannot change %s from %q to %q", key, old.attrs[key], cfg.attrs[key])
	}
	return nil
}

func (p nullProvider) validate(cfg, old *config.Config) (*environConfig, error) {
	// Check for valid changes for the base config values.
	if err := config.Validate(cfg, old); err != nil {
		return nil, err
	}
	validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults)
	if err != nil {
		return nil, err
	}
	envConfig := newEnvironConfig(cfg, validated)
	if envConfig.bootstrapHost() == "" {
		return nil, errNoBootstrapHost
	}
	// Check various immutable attributes.
	if old != nil {
		oldEnvConfig, err := p.validate(old, nil)
		if err != nil {
			return nil, err
		}
		for _, key := range [...]string{
			"bootstrap-user",
			"bootstrap-host",
			"storage-listen-ip",
		} {
			if err = checkImmutableString(envConfig, oldEnvConfig, key); err != nil {
				return nil, err
			}
		}
		oldPort, newPort := oldEnvConfig.storagePort(), envConfig.storagePort()
		if oldPort != newPort {
			return nil, fmt.Errorf("cannot change storage-port from %q to %q", oldPort, newPort)
		}
	}
	return envConfig, nil
}

func (p nullProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) {
	envConfig, err := p.validate(cfg, old)
	if err != nil {
		return nil, err
	}
	return cfg.Apply(envConfig.attrs)
}

func (_ nullProvider) BoilerplateConfig() string {
	return `
    "null":
        type: "null"
        admin-secret: {{rand}}
        ## set bootstrap-host to the host where the bootstrap machine agent
        ## should be provisioned.
        bootstrap-host:
        ## set the login user to bootstrap the machine as. If left blank,
        ## juju will connect to the bootstrap machine as the current user.
        # bootstrap-user:
        ## set the IP address for the bootstrap machine to listen on for
        ## storage requests. If left blank, storage will be served on all
        ## network interfaces.
        # storage-listen-ip:
        # storage-port: 8040
`
}

func (_ nullProvider) SecretAttrs(cfg *config.Config) (map[string]string, error) {
	return make(map[string]string), nil
}

func (_ nullProvider) PublicAddress() (string, error) {
	// TODO(axw) 2013-09-10 bug #1222643
	//
	// eth0 may not be the desired interface for traffic to route
	// through. We should somehow make this configurable, and
	// possibly also record the IP resolved during manual bootstrap.
	return utils.GetAddressForInterface("eth0")
}

func (p nullProvider) PrivateAddress() (string, error) {
	return p.PublicAddress()
}
<commit_msg>provider/null: fix boilerplate<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package null

import (
	"errors"
	"fmt"

	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/environs/config"
	"launchpad.net/juju-core/provider"
	"launchpad.net/juju-core/utils"
)

type nullProvider struct{}

func init() {
	environs.RegisterProvider(provider.Null, nullProvider{})
}

var errNoBootstrapHost = errors.New("bootstrap-host must be specified")

func (p nullProvider) Prepare(cfg *config.Config) (environs.Environ, error) {
	return p.Open(cfg)
}

func (p nullProvider) Open(cfg *config.Config) (environs.Environ, error) {
	envConfig, err := p.validate(cfg, nil)
	if err != nil {
		return nil, err
	}
	return &nullEnviron{cfg: envConfig}, nil
}

func checkImmutableString(cfg, old *environConfig, key string) error {
	if old.attrs[key] != cfg.attrs[key] {
		return fmt.Errorf("cannot change %s from %q to %q", key, old.attrs[key], cfg.attrs[key])
	}
	return nil
}

func (p nullProvider) validate(cfg, old *config.Config) (*environConfig, error) {
	// Check for valid changes for the base config values.
	if err := config.Validate(cfg, old); err != nil {
		return nil, err
	}
	validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults)
	if err != nil {
		return nil, err
	}
	envConfig := newEnvironConfig(cfg, validated)
	if envConfig.bootstrapHost() == "" {
		return nil, errNoBootstrapHost
	}
	// Check various immutable attributes.
	if old != nil {
		oldEnvConfig, err := p.validate(old, nil)
		if err != nil {
			return nil, err
		}
		for _, key := range [...]string{
			"bootstrap-user",
			"bootstrap-host",
			"storage-listen-ip",
		} {
			if err = checkImmutableString(envConfig, oldEnvConfig, key); err != nil {
				return nil, err
			}
		}
		oldPort, newPort := oldEnvConfig.storagePort(), envConfig.storagePort()
		if oldPort != newPort {
			return nil, fmt.Errorf("cannot change storage-port from %q to %q", oldPort, newPort)
		}
	}
	return envConfig, nil
}

func (p nullProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) {
	envConfig, err := p.validate(cfg, old)
	if err != nil {
		return nil, err
	}
	return cfg.Apply(envConfig.attrs)
}

func (_ nullProvider) BoilerplateConfig() string {
	return `"null":
        type: "null"
        admin-secret: {{rand}}
        ## set bootstrap-host to the host where the bootstrap machine agent
        ## should be provisioned.
        bootstrap-host:
        ## set the login user to bootstrap the machine as. If left blank,
        ## juju will connect to the bootstrap machine as the current user.
        # bootstrap-user:
        ## set the IP address for the bootstrap machine to listen on for
        ## storage requests. If left blank, storage will be served on all
        ## network interfaces.
        # storage-listen-ip:
        # storage-port: 8040

`
}

func (_ nullProvider) SecretAttrs(cfg *config.Config) (map[string]string, error) {
	return make(map[string]string), nil
}

func (_ nullProvider) PublicAddress() (string, error) {
	// TODO(axw) 2013-09-10 bug #1222643
	//
	// eth0 may not be the desired interface for traffic to route
	// through. We should somehow make this configurable, and
	// possibly also record the IP resolved during manual bootstrap.
	return utils.GetAddressForInterface("eth0")
}

func (p nullProvider) PrivateAddress() (string, error) {
	return p.PublicAddress()
}
<|endoftext|>