<commit_before>package goarmorapi

import (
	"context"
	"encoding/json"
	"errors"
	"strings"
	"time"

	"github.com/armor5games/goarmor/goarmorconfigs"
)

type JSONRequest struct {
	Payload interface{} `json:",omitempty"`
	Time    uint64      `json:",omitempty"`
}

type JSONResponse struct {
	Success bool
	Errors  []*ErrorJSON `json:",omitempty"`
	Payload interface{}  `json:",omitempty"`
	Time    uint64       `json:",omitempty"`
}

type ErrorJSON struct {
	Code     uint64
	Error    error             `json:"Message,omitempty"`
	Public   bool              `json:"-"`
	Severity ErrorJSONSeverity `json:"-"`
}

type ErrorJSONSeverity uint64

const (
	ErrSeverityDebug ErrorJSONSeverity = iota
	ErrSeverityInfo
	ErrSeverityWarn
	ErrSeverityError
	ErrSeverityFatal
	ErrSeverityPanic
)

type ResponseErrorer interface {
	ResponseErrors() []*ErrorJSON
}

func (e *ErrorJSON) MarshalJSON() ([]byte, error) {
	var m string

	if e.Error != nil {
		m = e.Error.Error()
	}

	return json.Marshal(&struct {
		Code    uint64
		Message string `json:",omitempty"`
	}{
		Code:    e.Code,
		Message: m})
}

func (e *ErrorJSON) UnmarshalJSON(b []byte) error {
	s := &struct {
		Code    uint64
		Message string
	}{}

	if err := json.Unmarshal(b, &s); err != nil {
		return err
	}

	e.Code = s.Code

	if s.Message != "" {
		e.Error = errors.New(s.Message)
	}

	return nil
}

func (j *JSONResponse) KV() (KV, error) {
	if j == nil {
		return nil, errors.New("empty api response")
	}

	if len(j.Errors) == 0 {
		return nil, errors.New("empty key values")
	}

	kv := NewKV()

	for _, e := range j.Errors {
		if e.Code != KVAPIErrorCode {
			continue
		}

		if e.Error.Error() == "" {
			return nil, errors.New("empty kv")
		}

		x := strings.SplitN(e.Error.Error(), ":", 2)
		if len(x) != 2 {
			return nil, errors.New("bad kv format")
		}

		kv[x[0]] = x[1]
	}

	if len(kv) == 0 {
		return nil, errors.New("empty kv")
	}

	return kv, nil
}

func NewJSONRequest(
	ctx context.Context,
	responsePayload interface{}) (*JSONRequest, error) {
	return &JSONRequest{
		Payload: responsePayload,
		Time:    uint64(time.Now().Unix())}, nil
}

func NewJSONResponse(
	ctx context.Context,
	isSuccess bool,
	responsePayload interface{},
	responseErrorer ResponseErrorer,
	errs ...*ErrorJSON) (*JSONResponse, error) {
	config, ok := ctx.Value(CtxKeyConfig).(goarmorconfigs.Configer)
	if !ok {
		return nil, errors.New("context.Value fn error")
	}

	errs = append(errs, responseErrorer.ResponseErrors()...)

	var publicErrors []*ErrorJSON

	if config.ServerDebuggingLevel() > 0 {
		for _, x := range errs {
			publicErrors = append(publicErrors,
				&ErrorJSON{
					Code:     x.Code,
					Error:    errors.New(x.Error.Error()),
					Public:   x.Public,
					Severity: x.Severity})
		}

	} else {
		isKVRemoved := false

		for _, x := range errs {
			if x.Public {
				publicErrors = append(publicErrors,
					&ErrorJSON{
						Code:     x.Code,
						Error:    errors.New(x.Error.Error()),
						Public:   x.Public,
						Severity: x.Severity})

				continue
			}

			if x.Code == KVAPIErrorCode {
				isKVRemoved = true

				continue
			}

			publicErrors = append(publicErrors,
				&ErrorJSON{Code: x.Code, Severity: x.Severity})
		}

		if isKVRemoved {
			// Add empty (only with "code") "ErrorJSON" structure in order to be able to
			// determine was an key-values in hadler's response.
			publicErrors = append(publicErrors, &ErrorJSON{Code: KVAPIErrorCode})
		}
	}

	return &JSONResponse{
		Success: isSuccess,
		Errors:  publicErrors,
		Payload: responsePayload,
		Time:    uint64(time.Now().Unix())}, nil
}
<commit_msg>docs: todo: rename "goarmorapi.ErrorJSON.Error" to "Err"<commit_after>package goarmorapi

import (
	"context"
	"encoding/json"
	"errors"
	"strings"
	"time"

	"github.com/armor5games/goarmor/goarmorconfigs"
)

type JSONRequest struct {
	Payload interface{} `json:",omitempty"`
	Time    uint64      `json:",omitempty"`
}

type JSONResponse struct {
	Success bool
	Errors  []*ErrorJSON `json:",omitempty"`
	Payload interface{}  `json:",omitempty"`
	Time    uint64       `json:",omitempty"`
}

type ErrorJSON struct {
	Code uint64
	// TODO: Rename "Error" to "Err"
	Error    error             `json:"Message,omitempty"`
	Public   bool              `json:"-"`
	Severity ErrorJSONSeverity `json:"-"`
}

type ErrorJSONSeverity uint64

const (
	ErrSeverityDebug ErrorJSONSeverity = iota
	ErrSeverityInfo
	ErrSeverityWarn
	ErrSeverityError
	ErrSeverityFatal
	ErrSeverityPanic
)

type ResponseErrorer interface {
	ResponseErrors() []*ErrorJSON
}

func (e *ErrorJSON) MarshalJSON() ([]byte, error) {
	var m string

	if e.Error != nil {
		m = e.Error.Error()
	}

	return json.Marshal(&struct {
		Code    uint64
		Message string `json:",omitempty"`
	}{
		Code:    e.Code,
		Message: m})
}

func (e *ErrorJSON) UnmarshalJSON(b []byte) error {
	s := &struct {
		Code    uint64
		Message string
	}{}

	if err := json.Unmarshal(b, &s); err != nil {
		return err
	}

	e.Code = s.Code

	if s.Message != "" {
		e.Error = errors.New(s.Message)
	}

	return nil
}

func (j *JSONResponse) KV() (KV, error) {
	if j == nil {
		return nil, errors.New("empty api response")
	}

	if len(j.Errors) == 0 {
		return nil, errors.New("empty key values")
	}

	kv := NewKV()

	for _, e := range j.Errors {
		if e.Code != KVAPIErrorCode {
			continue
		}

		if e.Error.Error() == "" {
			return nil, errors.New("empty kv")
		}

		x := strings.SplitN(e.Error.Error(), ":", 2)
		if len(x) != 2 {
			return nil, errors.New("bad kv format")
		}

		kv[x[0]] = x[1]
	}

	if len(kv) == 0 {
		return nil, errors.New("empty kv")
	}

	return kv, nil
}

func NewJSONRequest(
	ctx context.Context,
	responsePayload interface{}) (*JSONRequest, error) {
	return &JSONRequest{
		Payload: responsePayload,
		Time:    uint64(time.Now().Unix())}, nil
}

func NewJSONResponse(
	ctx context.Context,
	isSuccess bool,
	responsePayload interface{},
	responseErrorer ResponseErrorer,
	errs ...*ErrorJSON) (*JSONResponse, error) {
	config, ok := ctx.Value(CtxKeyConfig).(goarmorconfigs.Configer)
	if !ok {
		return nil, errors.New("context.Value fn error")
	}

	errs = append(errs, responseErrorer.ResponseErrors()...)

	var publicErrors []*ErrorJSON

	if config.ServerDebuggingLevel() > 0 {
		for _, x := range errs {
			publicErrors = append(publicErrors,
				&ErrorJSON{
					Code:     x.Code,
					Error:    errors.New(x.Error.Error()),
					Public:   x.Public,
					Severity: x.Severity})
		}

	} else {
		isKVRemoved := false

		for _, x := range errs {
			if x.Public {
				publicErrors = append(publicErrors,
					&ErrorJSON{
						Code:     x.Code,
						Error:    errors.New(x.Error.Error()),
						Public:   x.Public,
						Severity: x.Severity})

				continue
			}

			if x.Code == KVAPIErrorCode {
				isKVRemoved = true

				continue
			}

			publicErrors = append(publicErrors,
				&ErrorJSON{Code: x.Code, Severity: x.Severity})
		}

		if isKVRemoved {
			// Add empty (only with "code") "ErrorJSON" structure in order to be able to
			// determine was an key-values in hadler's response.
			publicErrors = append(publicErrors, &ErrorJSON{Code: KVAPIErrorCode})
		}
	}

	return &JSONResponse{
		Success: isSuccess,
		Errors:  publicErrors,
		Payload: responsePayload,
		Time:    uint64(time.Now().Unix())}, nil
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"
	"regexp"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/sagemaker"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
	"github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags"
	"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/sagemaker/finder"
	"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/sagemaker/waiter"
)

func resourceAwsSagemakerImage() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsSagemakerImageCreate,
		Read:   resourceAwsSagemakerImageRead,
		Update: resourceAwsSagemakerImageUpdate,
		Delete: resourceAwsSagemakerImageDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"arn": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"image_name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
				ValidateFunc: validation.All(
					validation.StringLenBetween(1, 63),
					validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9](-*[a-zA-Z0-9])*$`), "Valid characters are a-z, A-Z, 0-9, and - (hyphen)."),
				),
			},
			"role_arn": {
				Type:         schema.TypeString,
				Required:     true,
				ValidateFunc: validateArn,
			},
			"display_name": {
				Type:         schema.TypeString,
				Optional:     true,
				ValidateFunc: validation.StringLenBetween(1, 128),
			},
			"description": {
				Type:         schema.TypeString,
				Optional:     true,
				ValidateFunc: validation.StringLenBetween(1, 512),
			},
			"tags": tagsSchema(),
		},
	}
}

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

	name := d.Get("image_name").(string)
	input := &sagemaker.CreateImageInput{
		ImageName: aws.String(name),
		RoleArn:   aws.String(d.Get("role_arn").(string)),
	}

	if v, ok := d.GetOk("display_name"); ok {
		input.DisplayName = aws.String(v.(string))
	}

	if v, ok := d.GetOk("description"); ok {
		input.Description = aws.String(v.(string))
	}

	if v, ok := d.GetOk("tags"); ok {
		input.Tags = keyvaluetags.New(v.(map[string]interface{})).IgnoreAws().SagemakerTags()
	}

	log.Printf("[DEBUG] sagemaker Image create config: %#v", *input)
	_, err := conn.CreateImage(input)
	if err != nil {
		return fmt.Errorf("error creating SageMaker Image: %w", err)
	}

	d.SetId(name)

	if _, err := waiter.ImageCreated(conn, d.Id()); err != nil {
		return fmt.Errorf("error waiting for sagemaker image (%s) to create: %w", d.Id(), err)
	}

	return resourceAwsSagemakerImageRead(d, meta)
}

func resourceAwsSagemakerImageRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).sagemakerconn
	ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig

	image, err := finder.ImageByName(conn, d.Id())
	if err != nil {
		if isAWSErr(err, sagemaker.ErrCodeResourceNotFound, "No Image with the name") {
			d.SetId("")
			log.Printf("[WARN] Unable to find SageMaker Image (%s); removing from state", d.Id())
			return nil
		}
		return fmt.Errorf("error reading SageMaker Image (%s): %w", d.Id(), err)

	}

	arn := aws.StringValue(image.ImageArn)
	d.Set("image_name", image.ImageName)
	d.Set("arn", arn)
	d.Set("role_arn", image.RoleArn)
	d.Set("display_name", image.DisplayName)
	d.Set("description", image.Description)

	tags, err := keyvaluetags.SagemakerListTags(conn, arn)

	if err != nil {
		return fmt.Errorf("error listing tags for Sagemaker Image (%s): %w", d.Id(), err)
	}

	if err := d.Set("tags", tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil {
		return fmt.Errorf("error setting tags: %w", err)
	}

	return nil
}

func resourceAwsSagemakerImageUpdate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).sagemakerconn
	needsUpdate := false

	input := &sagemaker.UpdateImageInput{
		ImageName: aws.String(d.Id()),
	}

	var deleteProperties []*string

	if d.HasChange("role_arn") {
		input.RoleArn= aws.String(d.Get("role_arn").(string))
	}

	if d.HasChange("description") {
		if v, ok := d.GetOk("description"); ok {
			input.Description = aws.String(v.(string))
		} else {
			deleteProperties = append(deleteProperties, aws.String("Description"))
			input.DeleteProperties = deleteProperties
		}
		needsUpdate = true
	}

	if d.HasChange("display_name") {
		if v, ok := d.GetOk("display_name"); ok {
			input.DisplayName = aws.String(v.(string))
		} else {
			deleteProperties = append(deleteProperties, aws.String("DisplayName"))
			input.DeleteProperties = deleteProperties
		}
		needsUpdate = true
	}

	if needsUpdate {
		log.Printf("[DEBUG] sagemaker Image update config: %#v", *input)
		_, err := conn.UpdateImage(input)
		if err != nil {
			return fmt.Errorf("error updating SageMaker Image: %w", err)
		}

		if _, err := waiter.ImageCreated(conn, d.Id()); err != nil {
			return fmt.Errorf("error waiting for sagemaker image (%s) to update: %w", d.Id(), err)
		}
	}

	if d.HasChange("tags") {
		o, n := d.GetChange("tags")

		if err := keyvaluetags.SagemakerUpdateTags(conn, d.Get("arn").(string), o, n); err != nil {
			return fmt.Errorf("error updating Sagemaker Image (%s) tags: %s", d.Id(), err)
		}
	}

	return resourceAwsSagemakerImageRead(d, meta)
}

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

	input := &sagemaker.DeleteImageInput{
		ImageName: aws.String(d.Id()),
	}

	if _, err := conn.DeleteImage(input); err != nil {
		if isAWSErr(err, sagemaker.ErrCodeResourceNotFound, "No Image with the name") {
			return nil
		}
		return fmt.Errorf("error deleting SageMaker Image (%s): %w", d.Id(), err)
	}

	if _, err := waiter.ImageDeleted(conn, d.Id()); err != nil {
		if isAWSErr(err, sagemaker.ErrCodeResourceNotFound, "No Image with the name") {
			return nil
		}
		return fmt.Errorf("error waiting for sagemaker image (%s) to delete: %w", d.Id(), err)

	}

	return nil
}
<commit_msg>Apply suggestions from code review<commit_after>package aws

import (
	"fmt"
	"log"
	"regexp"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/sagemaker"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
	"github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags"
	"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/sagemaker/finder"
	"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/sagemaker/waiter"
)

func resourceAwsSagemakerImage() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsSagemakerImageCreate,
		Read:   resourceAwsSagemakerImageRead,
		Update: resourceAwsSagemakerImageUpdate,
		Delete: resourceAwsSagemakerImageDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"arn": {
				Type:     schema.TypeString,
				Computed: true,
			},

			"image_name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
				ValidateFunc: validation.All(
					validation.StringLenBetween(1, 63),
					validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9](-*[a-zA-Z0-9])*$`), "Valid characters are a-z, A-Z, 0-9, and - (hyphen)."),
				),
			},
			"role_arn": {
				Type:         schema.TypeString,
				Required:     true,
				ValidateFunc: validateArn,
			},
			"display_name": {
				Type:         schema.TypeString,
				Optional:     true,
				ValidateFunc: validation.StringLenBetween(1, 128),
			},
			"description": {
				Type:         schema.TypeString,
				Optional:     true,
				ValidateFunc: validation.StringLenBetween(1, 512),
			},
			"tags": tagsSchema(),
		},
	}
}

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

	name := d.Get("image_name").(string)
	input := &sagemaker.CreateImageInput{
		ImageName: aws.String(name),
		RoleArn:   aws.String(d.Get("role_arn").(string)),
	}

	if v, ok := d.GetOk("display_name"); ok {
		input.DisplayName = aws.String(v.(string))
	}

	if v, ok := d.GetOk("description"); ok {
		input.Description = aws.String(v.(string))
	}

	if v, ok := d.GetOk("tags"); ok {
		input.Tags = keyvaluetags.New(v.(map[string]interface{})).IgnoreAws().SagemakerTags()
	}

	log.Printf("[DEBUG] sagemaker Image create config: %#v", *input)
	_, err := conn.CreateImage(input)
	if err != nil {
		return fmt.Errorf("error creating SageMaker Image: %w", err)
	}

	d.SetId(name)

	if _, err := waiter.ImageCreated(conn, d.Id()); err != nil {
		return fmt.Errorf("error waiting for SageMaker Image (%s) to create: %w", d.Id(), err)
	}

	return resourceAwsSagemakerImageRead(d, meta)
}

func resourceAwsSagemakerImageRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).sagemakerconn
	ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig

	image, err := finder.ImageByName(conn, d.Id())
	if err != nil {
		if isAWSErr(err, sagemaker.ErrCodeResourceNotFound, "No Image with the name") {
			d.SetId("")
			log.Printf("[WARN] Unable to find SageMaker Image (%s); removing from state", d.Id())
			return nil
		}
		return fmt.Errorf("error reading SageMaker Image (%s): %w", d.Id(), err)

	}

	arn := aws.StringValue(image.ImageArn)
	d.Set("image_name", image.ImageName)
	d.Set("arn", arn)
	d.Set("role_arn", image.RoleArn)
	d.Set("display_name", image.DisplayName)
	d.Set("description", image.Description)

	tags, err := keyvaluetags.SagemakerListTags(conn, arn)

	if err != nil {
		return fmt.Errorf("error listing tags for SageMaker Image (%s): %w", d.Id(), err)
	}

	if err := d.Set("tags", tags.IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil {
		return fmt.Errorf("error setting tags: %w", err)
	}

	return nil
}

func resourceAwsSagemakerImageUpdate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).sagemakerconn
	needsUpdate := false

	input := &sagemaker.UpdateImageInput{
		ImageName: aws.String(d.Id()),
	}

	var deleteProperties []*string

	if d.HasChange("role_arn") {
		input.RoleArn= aws.String(d.Get("role_arn").(string))
	}

	if d.HasChange("description") {
		if v, ok := d.GetOk("description"); ok {
			input.Description = aws.String(v.(string))
		} else {
			deleteProperties = append(deleteProperties, aws.String("Description"))
			input.DeleteProperties = deleteProperties
		}
		needsUpdate = true
	}

	if d.HasChange("display_name") {
		if v, ok := d.GetOk("display_name"); ok {
			input.DisplayName = aws.String(v.(string))
		} else {
			deleteProperties = append(deleteProperties, aws.String("DisplayName"))
			input.DeleteProperties = deleteProperties
		}
		needsUpdate = true
	}

	if needsUpdate {
		log.Printf("[DEBUG] sagemaker Image update config: %#v", *input)
		_, err := conn.UpdateImage(input)
		if err != nil {
			return fmt.Errorf("error updating SageMaker Image: %w", err)
		}

		if _, err := waiter.ImageCreated(conn, d.Id()); err != nil {
			return fmt.Errorf("error waiting for SageMaker Image (%s) to update: %w", d.Id(), err)
		}
	}

	if d.HasChange("tags") {
		o, n := d.GetChange("tags")

		if err := keyvaluetags.SagemakerUpdateTags(conn, d.Get("arn").(string), o, n); err != nil {
			return fmt.Errorf("error updating SageMaker Image (%s) tags: %s", d.Id(), err)
		}
	}

	return resourceAwsSagemakerImageRead(d, meta)
}

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

	input := &sagemaker.DeleteImageInput{
		ImageName: aws.String(d.Id()),
	}

	if _, err := conn.DeleteImage(input); err != nil {
		if isAWSErr(err, sagemaker.ErrCodeResourceNotFound, "No Image with the name") {
			return nil
		}
		return fmt.Errorf("error deleting SageMaker Image (%s): %w", d.Id(), err)
	}

	if _, err := waiter.ImageDeleted(conn, d.Id()); err != nil {
		if isAWSErr(err, sagemaker.ErrCodeResourceNotFound, "No Image with the name") {
			return nil
		}
		return fmt.Errorf("error waiting for sagemaker image (%s) to delete: %w", d.Id(), err)

	}

	return nil
}
<|endoftext|>


<commit_before>package notifiers

import (
	"time"

	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/components/simplejson"
	"github.com/grafana/grafana/pkg/log"
	m "github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/services/alerting"
)

func init() {
	alerting.RegisterNotifier(&alerting.NotifierPlugin{
		Type:        "prometheus-alertmanager",
		Name:        "Prometheus Alertmanager",
		Description: "Sends alert to Prometheus Alertmanager",
		Factory:     NewAlertmanagerNotifier,
		OptionsTemplate: `

Alertmanager settings

        Url
        <input type="text" required class="gf-form-input max-width-26" ng-model="ctrl.model.settings.url" placeholder="http://localhost:9093"></input>

    `,
	})
}

func NewAlertmanagerNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
	url := model.Settings.Get("url").MustString()
	if url == "" {
		return nil, alerting.ValidationError{Reason: "Could not find url property in settings"}
	}

	return &AlertmanagerNotifier{
		NotifierBase: NewNotifierBase(model.Id, model.IsDefault, model.Name, model.Type, model.Settings),
		Url:          url,
		log:          log.New("alerting.notifier.prometheus-alertmanager"),
	}, nil
}

type AlertmanagerNotifier struct {
	NotifierBase
	Url string
	log log.Logger
}

func (this *AlertmanagerNotifier) ShouldNotify(evalContext *alerting.EvalContext) bool {
	this.log.Debug("Should notify", "ruleId", evalContext.Rule.Id, "state", evalContext.Rule.State, "previousState", evalContext.PrevAlertState)

	// Do not notify when we become OK for the first time.
	if (evalContext.PrevAlertState == m.AlertStatePending) && (evalContext.Rule.State == m.AlertStateOK) {
		return false
	}
	// Notify on Alerting -> OK to resolve before alertmanager timeout.
	if (evalContext.PrevAlertState == m.AlertStateAlerting) && (evalContext.Rule.State == m.AlertStateOK) {
		return true
	}
	return evalContext.Rule.State == m.AlertStateAlerting
}

func (this *AlertmanagerNotifier) createAlert(evalContext *alerting.EvalContext, match *alerting.EvalMatch, ruleUrl string) *simplejson.Json {
	alertJSON := simplejson.New()
	alertJSON.Set("startsAt", evalContext.StartTime.UTC().Format(time.RFC3339))
	if evalContext.Rule.State == m.AlertStateOK {
		alertJSON.Set("endsAt", time.Now().UTC().Format(time.RFC3339))
	}
	alertJSON.Set("generatorURL", ruleUrl)

	// Annotations (summary and description are very commonly used).
	alertJSON.SetPath([]string{"annotations", "summary"}, evalContext.Rule.Name)
	description := ""
	if evalContext.Rule.Message != "" {
		description += evalContext.Rule.Message
	}
	if evalContext.Error != nil {
		if description != "" {
			description += "\n"
		}
		description += "Error: " + evalContext.Error.Error()
	}
	if description != "" {
		alertJSON.SetPath([]string{"annotations", "description"}, description)
	}
	if evalContext.ImagePublicUrl != "" {
		alertJSON.SetPath([]string{"annotations", "image"}, evalContext.ImagePublicUrl)
	}

	// Labels (from metrics tags + mandatory alertname).
	tags := make(map[string]string)
	if match != nil {
		if len(match.Tags) == 0 {
			tags["metric"] = match.Metric
		} else {
			for k, v := range match.Tags {
				tags[k] = v
			}
		}
	}
	tags["alertname"] = evalContext.Rule.Name
	alertJSON.Set("labels", tags)
	return alertJSON
}

func (this *AlertmanagerNotifier) Notify(evalContext *alerting.EvalContext) error {
	this.log.Info("Creating Altermanager alert", "ruleId", evalContext.Rule.Id, "notification", this.Name)

	ruleUrl, err := evalContext.GetRuleUrl()
	if err != nil {
		this.log.Error("Failed get rule link", "error", err)
		return err
	}

	// Send one alert per matching series.
	alerts := make([]interface{}, 0)
	for _, match := range evalContext.EvalMatches {
		alert := this.createAlert(evalContext, match, ruleUrl)
		alerts = append(alerts, alert)
	}

	// This happens on ExecutionError or NoData
	if len(alerts) == 0 {
		alert := this.createAlert(evalContext, nil, ruleUrl)
		alerts = append(alerts, alert)
	}

	bodyJSON := simplejson.NewFromAny(alerts)
	body, _ := bodyJSON.MarshalJSON()

	cmd := &m.SendWebhookSync{
		Url:        this.Url + "/api/v1/alerts",
		HttpMethod: "POST",
		Body:       string(body),
	}

	if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
		this.log.Error("Failed to send alertmanager", "error", err, "alertmanager", this.Name)
		return err
	}

	return nil
}
<commit_msg>alertmanager: /Creating/Sending/<commit_after>package notifiers

import (
	"time"

	"github.com/grafana/grafana/pkg/bus"
	"github.com/grafana/grafana/pkg/components/simplejson"
	"github.com/grafana/grafana/pkg/log"
	m "github.com/grafana/grafana/pkg/models"
	"github.com/grafana/grafana/pkg/services/alerting"
)

func init() {
	alerting.RegisterNotifier(&alerting.NotifierPlugin{
		Type:        "prometheus-alertmanager",
		Name:        "Prometheus Alertmanager",
		Description: "Sends alert to Prometheus Alertmanager",
		Factory:     NewAlertmanagerNotifier,
		OptionsTemplate: `

Alertmanager settings

        Url
        <input type="text" required class="gf-form-input max-width-26" ng-model="ctrl.model.settings.url" placeholder="http://localhost:9093"></input>

    `,
	})
}

func NewAlertmanagerNotifier(model *m.AlertNotification) (alerting.Notifier, error) {
	url := model.Settings.Get("url").MustString()
	if url == "" {
		return nil, alerting.ValidationError{Reason: "Could not find url property in settings"}
	}

	return &AlertmanagerNotifier{
		NotifierBase: NewNotifierBase(model.Id, model.IsDefault, model.Name, model.Type, model.Settings),
		Url:          url,
		log:          log.New("alerting.notifier.prometheus-alertmanager"),
	}, nil
}

type AlertmanagerNotifier struct {
	NotifierBase
	Url string
	log log.Logger
}

func (this *AlertmanagerNotifier) ShouldNotify(evalContext *alerting.EvalContext) bool {
	this.log.Debug("Should notify", "ruleId", evalContext.Rule.Id, "state", evalContext.Rule.State, "previousState", evalContext.PrevAlertState)

	// Do not notify when we become OK for the first time.
	if (evalContext.PrevAlertState == m.AlertStatePending) && (evalContext.Rule.State == m.AlertStateOK) {
		return false
	}
	// Notify on Alerting -> OK to resolve before alertmanager timeout.
	if (evalContext.PrevAlertState == m.AlertStateAlerting) && (evalContext.Rule.State == m.AlertStateOK) {
		return true
	}
	return evalContext.Rule.State == m.AlertStateAlerting
}

func (this *AlertmanagerNotifier) createAlert(evalContext *alerting.EvalContext, match *alerting.EvalMatch, ruleUrl string) *simplejson.Json {
	alertJSON := simplejson.New()
	alertJSON.Set("startsAt", evalContext.StartTime.UTC().Format(time.RFC3339))
	if evalContext.Rule.State == m.AlertStateOK {
		alertJSON.Set("endsAt", time.Now().UTC().Format(time.RFC3339))
	}
	alertJSON.Set("generatorURL", ruleUrl)

	// Annotations (summary and description are very commonly used).
	alertJSON.SetPath([]string{"annotations", "summary"}, evalContext.Rule.Name)
	description := ""
	if evalContext.Rule.Message != "" {
		description += evalContext.Rule.Message
	}
	if evalContext.Error != nil {
		if description != "" {
			description += "\n"
		}
		description += "Error: " + evalContext.Error.Error()
	}
	if description != "" {
		alertJSON.SetPath([]string{"annotations", "description"}, description)
	}
	if evalContext.ImagePublicUrl != "" {
		alertJSON.SetPath([]string{"annotations", "image"}, evalContext.ImagePublicUrl)
	}

	// Labels (from metrics tags + mandatory alertname).
	tags := make(map[string]string)
	if match != nil {
		if len(match.Tags) == 0 {
			tags["metric"] = match.Metric
		} else {
			for k, v := range match.Tags {
				tags[k] = v
			}
		}
	}
	tags["alertname"] = evalContext.Rule.Name
	alertJSON.Set("labels", tags)
	return alertJSON
}

func (this *AlertmanagerNotifier) Notify(evalContext *alerting.EvalContext) error {
	this.log.Info("Sending Alertmanager alert", "ruleId", evalContext.Rule.Id, "notification", this.Name)

	ruleUrl, err := evalContext.GetRuleUrl()
	if err != nil {
		this.log.Error("Failed get rule link", "error", err)
		return err
	}

	// Send one alert per matching series.
	alerts := make([]interface{}, 0)
	for _, match := range evalContext.EvalMatches {
		alert := this.createAlert(evalContext, match, ruleUrl)
		alerts = append(alerts, alert)
	}

	// This happens on ExecutionError or NoData
	if len(alerts) == 0 {
		alert := this.createAlert(evalContext, nil, ruleUrl)
		alerts = append(alerts, alert)
	}

	bodyJSON := simplejson.NewFromAny(alerts)
	body, _ := bodyJSON.MarshalJSON()

	cmd := &m.SendWebhookSync{
		Url:        this.Url + "/api/v1/alerts",
		HttpMethod: "POST",
		Body:       string(body),
	}

	if err := bus.DispatchCtx(evalContext.Ctx, cmd); err != nil {
		this.log.Error("Failed to send alertmanager", "error", err, "alertmanager", this.Name)
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package priority

import (
	"flag"
	"sort"
	"time"

	apiv1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/util/sets"
	vpa_types "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1"
	"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/annotations"
	vpa_api_util "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/vpa"
	"k8s.io/klog/v2"
)

var (
	defaultUpdateThreshold = flag.Float64("pod-update-threshold", 0.1, "Ignore updates that have priority lower than the value of this flag")

	podLifetimeUpdateThreshold = flag.Duration("in-recommendation-bounds-eviction-lifetime-threshold", time.Hour*12, "Pods that live for at least that long can be evicted even if their request is within the [MinRecommended...MaxRecommended] range")

	evictAfterOOMThreshold = flag.Duration("evict-after-oom-threshold", 10*time.Minute,
		`Evict pod that has only one container and it OOMed in less than
		evict-after-oom-threshold since start.`)
)

// UpdatePriorityCalculator is responsible for prioritizing updates on pods.
// It can returns a sorted list of pods in order of update priority.
// Update priority is proportional to fraction by which resources should be increased / decreased.
// i.e. pod with 10M current memory and recommendation 20M will have higher update priority
// than pod with 100M current memory and 150M recommendation (100% increase vs 50% increase)
type UpdatePriorityCalculator struct {
	vpa                     *vpa_types.VerticalPodAutoscaler
	pods                    []prioritizedPod
	config                  *UpdateConfig
	recommendationProcessor vpa_api_util.RecommendationProcessor
	priorityProcessor       PriorityProcessor
}

// UpdateConfig holds configuration for UpdatePriorityCalculator
type UpdateConfig struct {
	// MinChangePriority is the minimum change priority that will trigger a update.
	// TODO: should have separate for Mem and CPU?
	MinChangePriority float64
}

// NewUpdatePriorityCalculator creates new UpdatePriorityCalculator for the given VPA object
// an update config.
// If the vpa resource policy is nil, there will be no policy restriction on update.
// If the given update config is nil, default values are used.
func NewUpdatePriorityCalculator(vpa *vpa_types.VerticalPodAutoscaler,
	config *UpdateConfig,
	recommendationProcessor vpa_api_util.RecommendationProcessor,
	priorityProcessor PriorityProcessor) UpdatePriorityCalculator {
	if config == nil {
		config = &UpdateConfig{MinChangePriority: *defaultUpdateThreshold}
	}
	return UpdatePriorityCalculator{
		vpa:                     vpa,
		config:                  config,
		recommendationProcessor: recommendationProcessor,
		priorityProcessor:       priorityProcessor}
}

// AddPod adds pod to the UpdatePriorityCalculator.
func (calc *UpdatePriorityCalculator) AddPod(pod *apiv1.Pod, now time.Time) {
	processedRecommendation, _, err := calc.recommendationProcessor.Apply(calc.vpa.Status.Recommendation, calc.vpa.Spec.ResourcePolicy, calc.vpa.Status.Conditions, pod)
	if err != nil {
		klog.V(2).Infof("cannot process recommendation for pod %s/%s: %v", pod.Namespace, pod.Name, err)
		return
	}

	hasObservedContainers, vpaContainerSet := parseVpaObservedContainers(pod)

	updatePriority := calc.priorityProcessor.GetUpdatePriority(pod, calc.vpa, processedRecommendation)

	quickOOM := false
	for i := range pod.Status.ContainerStatuses {
		cs := &pod.Status.ContainerStatuses[i]
		if hasObservedContainers && !vpaContainerSet.Has(cs.Name) {
			// Containers not observed by Admission Controller are not supported
			// by the quick OOM logic.
			klog.V(4).Infof("Not listed in %s:%s. Skipping container %s quick OOM calculations",
				annotations.VpaObservedContainersLabel, pod.GetAnnotations()[annotations.VpaObservedContainersLabel], cs.Name)
			continue
		}
		crp := vpa_api_util.GetContainerResourcePolicy(cs.Name, calc.vpa.Spec.ResourcePolicy)
		if crp != nil && crp.Mode != nil && *crp.Mode == vpa_types.ContainerScalingModeOff {
			// Containers with ContainerScalingModeOff are not considered
			// during the quick OOM calculation.
			klog.V(4).Infof("Container with ContainerScalingModeOff. Skipping container %s quick OOM calculations", cs.Name)
			continue
		}
		terminationState := &cs.LastTerminationState
		if terminationState.Terminated != nil &&
			terminationState.Terminated.Reason == "OOMKilled" &&
			terminationState.Terminated.FinishedAt.Time.Sub(terminationState.Terminated.StartedAt.Time) < *evictAfterOOMThreshold {
			quickOOM = true
			klog.V(2).Infof("quick OOM detected in pod %v/%v, container %v", pod.Namespace, pod.Name, cs.Name)
		}
	}

	// The update is allowed in following cases:
	// - the request is outside the recommended range for some container.
	// - the pod lives for at least 24h and the resource diff is >= MinChangePriority.
	// - a vpa scaled container OOMed in less than evictAfterOOMThreshold.
	if !updatePriority.OutsideRecommendedRange && !quickOOM {
		if pod.Status.StartTime == nil {
			// TODO: Set proper condition on the VPA.
			klog.V(2).Infof("not updating pod %v/%v, missing field pod.Status.StartTime", pod.Namespace, pod.Name)
			return
		}
		if now.Before(pod.Status.StartTime.Add(*podLifetimeUpdateThreshold)) {
			klog.V(2).Infof("not updating a short-lived pod %v/%v, request within recommended range", pod.Namespace, pod.Name)
			return
		}
		if updatePriority.ResourceDiff < calc.config.MinChangePriority {
			klog.V(2).Infof("not updating pod %v/%v, resource diff too low: %v", pod.Namespace, pod.Name, updatePriority)
			return
		}
	}

	// If the pod has quick OOMed then evict only if the resources will change
	if quickOOM && updatePriority.ResourceDiff == 0 {
		klog.V(2).Infof("not updating pod %v/%v because resource would not change", pod.Namespace, pod.Name)
		return
	}
	klog.V(2).Infof("pod accepted for update %v/%v with priority %v", pod.Namespace, pod.Name, updatePriority.ResourceDiff)
	calc.pods = append(calc.pods, prioritizedPod{
		pod:            pod,
		priority:       updatePriority,
		recommendation: processedRecommendation})
}

// GetSortedPods returns a list of pods ordered by update priority (highest update priority first)
func (calc *UpdatePriorityCalculator) GetSortedPods(admission PodEvictionAdmission) []*apiv1.Pod {
	sort.Sort(byPriorityDesc(calc.pods))

	result := []*apiv1.Pod{}
	for _, podPrio := range calc.pods {
		if admission == nil || admission.Admit(podPrio.pod, podPrio.recommendation) {
			result = append(result, podPrio.pod)
		} else {
			klog.V(2).Infof("pod removed from update queue by PodEvictionAdmission: %v", podPrio.pod.Name)
		}
	}

	return result
}

func parseVpaObservedContainers(pod *apiv1.Pod) (bool, sets.String) {
	observedContainers, hasObservedContainers := pod.GetAnnotations()[annotations.VpaObservedContainersLabel]
	vpaContainerSet := sets.NewString()
	if hasObservedContainers {
		if containers, err := annotations.ParseVpaObservedContainersValue(observedContainers); err != nil {
			klog.Errorf("Vpa annotation %s failed to parse: %v", observedContainers, err)
			hasObservedContainers = false
		} else {
			vpaContainerSet.Insert(containers...)
		}
	}
	return hasObservedContainers, vpaContainerSet
}

type prioritizedPod struct {
	pod            *apiv1.Pod
	priority       PodPriority
	recommendation *vpa_types.RecommendedPodResources
}

// PodPriority contains data for a pod update that can be used to prioritize between updates.
type PodPriority struct {
	// Is any container outside of the recommended range.
	OutsideRecommendedRange bool
	// Does any container want to grow.
	ScaleUp bool
	// Relative difference between the total requested and total recommended resources.
	ResourceDiff float64
}

type byPriorityDesc []prioritizedPod

func (list byPriorityDesc) Len() int {
	return len(list)
}
func (list byPriorityDesc) Swap(i, j int) {
	list[i], list[j] = list[j], list[i]
}

// Less implements reverse ordering by priority (highest priority first).
// This means we return true if priority at index j is lower than at index i.
func (list byPriorityDesc) Less(i, j int) bool {
	return list[j].priority.Less(list[i].priority)
}

// Less returns true if p is lower than other.
func (p PodPriority) Less(other PodPriority) bool {
	// 1. If any container wants to grow, the pod takes precedence.
	// TODO: A better policy would be to prioritize scaling down when
	// (a) the pod is pending
	// (b) there is general resource shortage
	// and prioritize scaling up otherwise.
	if p.ScaleUp != other.ScaleUp {
		return other.ScaleUp
	}
	// 2. A pod with larger value of resourceDiff takes precedence.
	return p.ResourceDiff < other.ResourceDiff
}
<commit_msg>Turn VPA Logging Level to V4 for Not Updating Messages<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package priority

import (
	"flag"
	"sort"
	"time"

	apiv1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/util/sets"
	vpa_types "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1"
	"k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/annotations"
	vpa_api_util "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/utils/vpa"
	"k8s.io/klog/v2"
)

var (
	defaultUpdateThreshold = flag.Float64("pod-update-threshold", 0.1, "Ignore updates that have priority lower than the value of this flag")

	podLifetimeUpdateThreshold = flag.Duration("in-recommendation-bounds-eviction-lifetime-threshold", time.Hour*12, "Pods that live for at least that long can be evicted even if their request is within the [MinRecommended...MaxRecommended] range")

	evictAfterOOMThreshold = flag.Duration("evict-after-oom-threshold", 10*time.Minute,
		`Evict pod that has only one container and it OOMed in less than
		evict-after-oom-threshold since start.`)
)

// UpdatePriorityCalculator is responsible for prioritizing updates on pods.
// It can returns a sorted list of pods in order of update priority.
// Update priority is proportional to fraction by which resources should be increased / decreased.
// i.e. pod with 10M current memory and recommendation 20M will have higher update priority
// than pod with 100M current memory and 150M recommendation (100% increase vs 50% increase)
type UpdatePriorityCalculator struct {
	vpa                     *vpa_types.VerticalPodAutoscaler
	pods                    []prioritizedPod
	config                  *UpdateConfig
	recommendationProcessor vpa_api_util.RecommendationProcessor
	priorityProcessor       PriorityProcessor
}

// UpdateConfig holds configuration for UpdatePriorityCalculator
type UpdateConfig struct {
	// MinChangePriority is the minimum change priority that will trigger a update.
	// TODO: should have separate for Mem and CPU?
	MinChangePriority float64
}

// NewUpdatePriorityCalculator creates new UpdatePriorityCalculator for the given VPA object
// an update config.
// If the vpa resource policy is nil, there will be no policy restriction on update.
// If the given update config is nil, default values are used.
func NewUpdatePriorityCalculator(vpa *vpa_types.VerticalPodAutoscaler,
	config *UpdateConfig,
	recommendationProcessor vpa_api_util.RecommendationProcessor,
	priorityProcessor PriorityProcessor) UpdatePriorityCalculator {
	if config == nil {
		config = &UpdateConfig{MinChangePriority: *defaultUpdateThreshold}
	}
	return UpdatePriorityCalculator{
		vpa:                     vpa,
		config:                  config,
		recommendationProcessor: recommendationProcessor,
		priorityProcessor:       priorityProcessor}
}

// AddPod adds pod to the UpdatePriorityCalculator.
func (calc *UpdatePriorityCalculator) AddPod(pod *apiv1.Pod, now time.Time) {
	processedRecommendation, _, err := calc.recommendationProcessor.Apply(calc.vpa.Status.Recommendation, calc.vpa.Spec.ResourcePolicy, calc.vpa.Status.Conditions, pod)
	if err != nil {
		klog.V(2).Infof("cannot process recommendation for pod %s/%s: %v", pod.Namespace, pod.Name, err)
		return
	}

	hasObservedContainers, vpaContainerSet := parseVpaObservedContainers(pod)

	updatePriority := calc.priorityProcessor.GetUpdatePriority(pod, calc.vpa, processedRecommendation)

	quickOOM := false
	for i := range pod.Status.ContainerStatuses {
		cs := &pod.Status.ContainerStatuses[i]
		if hasObservedContainers && !vpaContainerSet.Has(cs.Name) {
			// Containers not observed by Admission Controller are not supported
			// by the quick OOM logic.
			klog.V(4).Infof("Not listed in %s:%s. Skipping container %s quick OOM calculations",
				annotations.VpaObservedContainersLabel, pod.GetAnnotations()[annotations.VpaObservedContainersLabel], cs.Name)
			continue
		}
		crp := vpa_api_util.GetContainerResourcePolicy(cs.Name, calc.vpa.Spec.ResourcePolicy)
		if crp != nil && crp.Mode != nil && *crp.Mode == vpa_types.ContainerScalingModeOff {
			// Containers with ContainerScalingModeOff are not considered
			// during the quick OOM calculation.
			klog.V(4).Infof("Container with ContainerScalingModeOff. Skipping container %s quick OOM calculations", cs.Name)
			continue
		}
		terminationState := &cs.LastTerminationState
		if terminationState.Terminated != nil &&
			terminationState.Terminated.Reason == "OOMKilled" &&
			terminationState.Terminated.FinishedAt.Time.Sub(terminationState.Terminated.StartedAt.Time) < *evictAfterOOMThreshold {
			quickOOM = true
			klog.V(2).Infof("quick OOM detected in pod %v/%v, container %v", pod.Namespace, pod.Name, cs.Name)
		}
	}

	// The update is allowed in following cases:
	// - the request is outside the recommended range for some container.
	// - the pod lives for at least 24h and the resource diff is >= MinChangePriority.
	// - a vpa scaled container OOMed in less than evictAfterOOMThreshold.
	if !updatePriority.OutsideRecommendedRange && !quickOOM {
		if pod.Status.StartTime == nil {
			// TODO: Set proper condition on the VPA.
			klog.V(4).Infof("not updating pod %v/%v, missing field pod.Status.StartTime", pod.Namespace, pod.Name)
			return
		}
		if now.Before(pod.Status.StartTime.Add(*podLifetimeUpdateThreshold)) {
			klog.V(4).Infof("not updating a short-lived pod %v/%v, request within recommended range", pod.Namespace, pod.Name)
			return
		}
		if updatePriority.ResourceDiff < calc.config.MinChangePriority {
			klog.V(4).Infof("not updating pod %v/%v, resource diff too low: %v", pod.Namespace, pod.Name, updatePriority)
			return
		}
	}

	// If the pod has quick OOMed then evict only if the resources will change
	if quickOOM && updatePriority.ResourceDiff == 0 {
		klog.V(4).Infof("not updating pod %v/%v because resource would not change", pod.Namespace, pod.Name)
		return
	}
	klog.V(2).Infof("pod accepted for update %v/%v with priority %v", pod.Namespace, pod.Name, updatePriority.ResourceDiff)
	calc.pods = append(calc.pods, prioritizedPod{
		pod:            pod,
		priority:       updatePriority,
		recommendation: processedRecommendation})
}

// GetSortedPods returns a list of pods ordered by update priority (highest update priority first)
func (calc *UpdatePriorityCalculator) GetSortedPods(admission PodEvictionAdmission) []*apiv1.Pod {
	sort.Sort(byPriorityDesc(calc.pods))

	result := []*apiv1.Pod{}
	for _, podPrio := range calc.pods {
		if admission == nil || admission.Admit(podPrio.pod, podPrio.recommendation) {
			result = append(result, podPrio.pod)
		} else {
			klog.V(2).Infof("pod removed from update queue by PodEvictionAdmission: %v", podPrio.pod.Name)
		}
	}

	return result
}

func parseVpaObservedContainers(pod *apiv1.Pod) (bool, sets.String) {
	observedContainers, hasObservedContainers := pod.GetAnnotations()[annotations.VpaObservedContainersLabel]
	vpaContainerSet := sets.NewString()
	if hasObservedContainers {
		if containers, err := annotations.ParseVpaObservedContainersValue(observedContainers); err != nil {
			klog.Errorf("Vpa annotation %s failed to parse: %v", observedContainers, err)
			hasObservedContainers = false
		} else {
			vpaContainerSet.Insert(containers...)
		}
	}
	return hasObservedContainers, vpaContainerSet
}

type prioritizedPod struct {
	pod            *apiv1.Pod
	priority       PodPriority
	recommendation *vpa_types.RecommendedPodResources
}

// PodPriority contains data for a pod update that can be used to prioritize between updates.
type PodPriority struct {
	// Is any container outside of the recommended range.
	OutsideRecommendedRange bool
	// Does any container want to grow.
	ScaleUp bool
	// Relative difference between the total requested and total recommended resources.
	ResourceDiff float64
}

type byPriorityDesc []prioritizedPod

func (list byPriorityDesc) Len() int {
	return len(list)
}
func (list byPriorityDesc) Swap(i, j int) {
	list[i], list[j] = list[j], list[i]
}

// Less implements reverse ordering by priority (highest priority first).
// This means we return true if priority at index j is lower than at index i.
func (list byPriorityDesc) Less(i, j int) bool {
	return list[j].priority.Less(list[i].priority)
}

// Less returns true if p is lower than other.
func (p PodPriority) Less(other PodPriority) bool {
	// 1. If any container wants to grow, the pod takes precedence.
	// TODO: A better policy would be to prioritize scaling down when
	// (a) the pod is pending
	// (b) there is general resource shortage
	// and prioritize scaling up otherwise.
	if p.ScaleUp != other.ScaleUp {
		return other.ScaleUp
	}
	// 2. A pod with larger value of resourceDiff takes precedence.
	return p.ResourceDiff < other.ResourceDiff
}
<|endoftext|>


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

package cshared_test

import (
	"debug/elf"
	"fmt"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
	"sync"
	"testing"
	"unicode"
)

// C compiler with args (from $(go env CC) $(go env GOGCCFLAGS)).
var cc []string

// An environment with GOPATH=$(pwd).
var gopathEnv []string

// ".exe" on Windows.
var exeSuffix string

var GOOS, GOARCH, GOROOT string
var installdir, androiddir string
var libSuffix, libgoname string

func TestMain(m *testing.M) {
	GOOS = goEnv("GOOS")
	GOARCH = goEnv("GOARCH")
	GOROOT = goEnv("GOROOT")

	if _, err := os.Stat(GOROOT); os.IsNotExist(err) {
		log.Fatalf("Unable able to find GOROOT at '%s'", GOROOT)
	}

	// Directory where cgo headers and outputs will be installed.
	// The installation directory format varies depending on the platform.
	installdir = path.Join("pkg", fmt.Sprintf("%s_%s_testcshared_shared", GOOS, GOARCH))
	switch GOOS {
	case "darwin":
		libSuffix = "dylib"
		installdir = path.Join("pkg", fmt.Sprintf("%s_%s_testcshared", GOOS, GOARCH))
	case "windows":
		libSuffix = "dll"
	default:
		libSuffix = "so"
	}

	androiddir = fmt.Sprintf("/data/local/tmp/testcshared-%d", os.Getpid())
	if GOOS == "android" {
		cmd := exec.Command("adb", "shell", "mkdir", "-p", androiddir)
		out, err := cmd.CombinedOutput()
		if err != nil {
			log.Fatalf("setupAndroid failed: %v\n%s\n", err, out)
		}
	}

	libgoname = "libgo." + libSuffix

	cc = []string{goEnv("CC")}

	out := goEnv("GOGCCFLAGS")
	quote := '\000'
	start := 0
	lastSpace := true
	backslash := false
	s := string(out)
	for i, c := range s {
		if quote == '\000' && unicode.IsSpace(c) {
			if !lastSpace {
				cc = append(cc, s[start:i])
				lastSpace = true
			}
		} else {
			if lastSpace {
				start = i
				lastSpace = false
			}
			if quote == '\000' && !backslash && (c == '"' || c == '\'') {
				quote = c
				backslash = false
			} else if !backslash && quote == c {
				quote = '\000'
			} else if (quote == '\000' || quote == '"') && !backslash && c == '\\' {
				backslash = true
			} else {
				backslash = false
			}
		}
	}
	if !lastSpace {
		cc = append(cc, s[start:])
	}

	switch GOOS {
	case "darwin":
		// For Darwin/ARM.
		// TODO(crawshaw): can we do better?
		cc = append(cc, []string{"-framework", "CoreFoundation", "-framework", "Foundation"}...)
	case "android":
		cc = append(cc, "-pie", "-fuse-ld=gold")
	}
	libgodir := GOOS + "_" + GOARCH
	switch GOOS {
	case "darwin":
		if GOARCH == "arm" || GOARCH == "arm64" {
			libgodir += "_shared"
		}
	case "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris":
		libgodir += "_shared"
	}
	cc = append(cc, "-I", filepath.Join("pkg", libgodir))

	// Build an environment with GOPATH=$(pwd)
	dir, err := os.Getwd()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(2)
	}
	gopathEnv = append(os.Environ(), "GOPATH="+dir)

	if GOOS == "windows" {
		exeSuffix = ".exe"
	}

	st := m.Run()

	os.Remove(libgoname)
	os.RemoveAll("pkg")
	cleanupHeaders()
	cleanupAndroid()

	os.Exit(st)
}

func goEnv(key string) string {
	out, err := exec.Command("go", "env", key).Output()
	if err != nil {
		fmt.Fprintf(os.Stderr, "go env %s failed:\n%s", key, err)
		fmt.Fprintf(os.Stderr, "%s", err.(*exec.ExitError).Stderr)
		os.Exit(2)
	}
	return strings.TrimSpace(string(out))
}

func cmdToRun(name string) string {
	return "./" + name + exeSuffix
}

func adbPush(t *testing.T, filename string) {
	if GOOS != "android" {
		return
	}
	args := []string{"adb", "push", filename, fmt.Sprintf("%s/%s", androiddir, filename)}
	cmd := exec.Command(args[0], args[1:]...)
	if out, err := cmd.CombinedOutput(); err != nil {
		t.Fatalf("adb command failed: %v\n%s\n", err, out)
	}
}

func adbRun(t *testing.T, env []string, adbargs ...string) string {
	if GOOS != "android" {
		t.Fatalf("trying to run adb command when operating system is not android.")
	}
	args := []string{"adb", "shell"}
	// Propagate LD_LIBRARY_PATH to the adb shell invocation.
	for _, e := range env {
		if strings.Index(e, "LD_LIBRARY_PATH=") != -1 {
			adbargs = append([]string{e}, adbargs...)
			break
		}
	}
	shellcmd := fmt.Sprintf("cd %s; %s", androiddir, strings.Join(adbargs, " "))
	args = append(args, shellcmd)
	cmd := exec.Command(args[0], args[1:]...)
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("adb command failed: %v\n%s\n", err, out)
	}
	return strings.Replace(string(out), "\r", "", -1)
}

func run(t *testing.T, env []string, args ...string) string {
	cmd := exec.Command(args[0], args[1:]...)
	cmd.Env = env
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("command failed: %v\n%v\n%s\n", args, err, out)
	} else {
		t.Logf("run: %v", args)
	}
	return string(out)
}

func runExe(t *testing.T, env []string, args ...string) string {
	if GOOS == "android" {
		return adbRun(t, env, args...)
	}
	return run(t, env, args...)
}

func runCC(t *testing.T, args ...string) string {
	return run(t, nil, append(cc, args...)...)
}

func createHeaders() error {
	args := []string{"go", "install", "-buildmode=c-shared",
		"-installsuffix", "testcshared", "libgo"}
	cmd := exec.Command(args[0], args[1:]...)
	cmd.Env = gopathEnv
	out, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("command failed: %v\n%v\n%s\n", args, err, out)
	}

	args = []string{"go", "build", "-buildmode=c-shared",
		"-installsuffix", "testcshared",
		"-o", libgoname,
		filepath.Join("src", "libgo", "libgo.go")}
	cmd = exec.Command(args[0], args[1:]...)
	cmd.Env = gopathEnv
	out, err = cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("command failed: %v\n%v\n%s\n", args, err, out)
	}

	if GOOS == "android" {
		args = []string{"adb", "push", libgoname, fmt.Sprintf("%s/%s", androiddir, libgoname)}
		cmd = exec.Command(args[0], args[1:]...)
		out, err = cmd.CombinedOutput()
		if err != nil {
			return fmt.Errorf("adb command failed: %v\n%s\n", err, out)
		}
	}

	return nil
}

var (
	headersOnce sync.Once
	headersErr  error
)

func createHeadersOnce(t *testing.T) {
	headersOnce.Do(func() {
		headersErr = createHeaders()
	})
	if headersErr != nil {
		t.Fatal(headersErr)
	}
}

func cleanupHeaders() {
	os.Remove("libgo.h")
}

func cleanupAndroid() {
	if GOOS != "android" {
		return
	}
	cmd := exec.Command("adb", "shell", "rm", "-rf", androiddir)
	out, err := cmd.CombinedOutput()
	if err != nil {
		log.Fatalf("cleanupAndroid failed: %v\n%s\n", err, out)
	}
}

// test0: exported symbols in shared lib are accessible.
func TestExportedSymbols(t *testing.T) {
	t.Parallel()

	cmd := "testp0"

	createHeadersOnce(t)

	runCC(t, "-I", installdir, "-o", cmd, "main0.c", libgoname)
	adbPush(t, cmd)

	defer os.Remove(cmd)

	out := run(t, append(gopathEnv, "LD_LIBRARY_PATH=."), cmdToRun(cmd))
	if strings.TrimSpace(out) != "PASS" {
		t.Error(out)
	}
}

// test1: shared library can be dynamically loaded and exported symbols are accessible.
func TestExportedSymbolsWithDynamicLoad(t *testing.T) {
	t.Parallel()

	cmd := "testp1"

	createHeadersOnce(t)

	runCC(t, "-o", cmd, "main1.c", "-ldl")
	adbPush(t, cmd)

	defer os.Remove(cmd)

	out := runExe(t, nil, cmdToRun(cmd), "./"+libgoname)
	if strings.TrimSpace(out) != "PASS" {
		t.Error(out)
	}
}

// test2: tests libgo2 which does not export any functions.
func TestUnexportedSymbols(t *testing.T) {
	t.Parallel()

	cmd := "testp2"
	libname := "libgo2." + libSuffix

	run(t,
		gopathEnv,
		"go", "build",
		"-buildmode=c-shared",
		"-installsuffix", "testcshared",
		"-o", libname, "libgo2",
	)
	adbPush(t, libname)

	linkFlags := "-Wl,--no-as-needed"
	if GOOS == "darwin" {
		linkFlags = ""
	}

	runCC(t, "-o", cmd, "main2.c", linkFlags, libname)
	adbPush(t, cmd)

	defer os.Remove(libname)
	defer os.Remove(cmd)

	out := run(t, append(gopathEnv, "LD_LIBRARY_PATH=."), cmdToRun(cmd))

	if strings.TrimSpace(out) != "PASS" {
		t.Error(out)
	}
}

// test3: tests main.main is exported on android.
func TestMainExportedOnAndroid(t *testing.T) {
	t.Parallel()

	if GOOS != "android" {
		return
	}

	cmd := "testp3"

	createHeadersOnce(t)

	runCC(t, "-o", cmd, "main3.c", "-ldl")
	adbPush(t, cmd)

	defer os.Remove(cmd)

	out := runExe(t, nil, cmdToRun(cmd), "./"+libgoname)
	if strings.TrimSpace(out) != "PASS" {
		t.Error(out)
	}
}

func testSignalHandlers(t *testing.T, pkgname, cfile, cmd string) {
	libname := pkgname + "." + libSuffix
	run(t,
		gopathEnv,
		"go", "build",
		"-buildmode=c-shared",
		"-installsuffix", "testcshared",
		"-o", libname, pkgname,
	)
	adbPush(t, libname)
	runCC(t, "-pthread", "-o", cmd, cfile, "-ldl")
	adbPush(t, cmd)

	defer os.Remove(libname)
	defer os.Remove(cmd)
	defer os.Remove(pkgname + ".h")

	bin := cmdToRun(cmd)
	out := runExe(t, nil, bin, "./"+libname)
	if strings.TrimSpace(out) != "PASS" {
		t.Error(run(t, nil, bin, libname, "verbose"))
	}
}

// test4: test signal handlers
func TestSignalHandlers(t *testing.T) {
	t.Parallel()
	testSignalHandlers(t, "libgo4", "main4.c", "testp4")
}

// test5: test signal handlers with os/signal.Notify
func TestSignalHandlersWithNotify(t *testing.T) {
	t.Parallel()
	testSignalHandlers(t, "libgo5", "main5.c", "testp5")
}

func TestPIE(t *testing.T) {
	t.Parallel()

	switch GOOS {
	case "linux", "android":
		break
	default:
		t.Logf("Skipping TestPIE on %s", GOOS)
		return
	}

	createHeadersOnce(t)

	f, err := elf.Open(libgoname)
	if err != nil {
		t.Fatalf("elf.Open failed: %v", err)
	}
	defer f.Close()

	ds := f.SectionByType(elf.SHT_DYNAMIC)
	if ds == nil {
		t.Fatalf("no SHT_DYNAMIC section")
	}
	d, err := ds.Data()
	if err != nil {
		t.Fatalf("can't read SHT_DYNAMIC contents: %v", err)
	}
	for len(d) > 0 {
		var tag elf.DynTag
		switch f.Class {
		case elf.ELFCLASS32:
			tag = elf.DynTag(f.ByteOrder.Uint32(d[:4]))
			d = d[8:]
		case elf.ELFCLASS64:
			tag = elf.DynTag(f.ByteOrder.Uint64(d[:8]))
			d = d[16:]
		}
		if tag == elf.DT_TEXTREL {
			t.Fatalf("%s has DT_TEXTREL flag", libgoname)
		}
	}
}
<commit_msg>misc/cgo/testcshared: actually run test executable on android<commit_after>// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package cshared_test

import (
	"debug/elf"
	"fmt"
	"log"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strings"
	"sync"
	"testing"
	"unicode"
)

// C compiler with args (from $(go env CC) $(go env GOGCCFLAGS)).
var cc []string

// An environment with GOPATH=$(pwd).
var gopathEnv []string

// ".exe" on Windows.
var exeSuffix string

var GOOS, GOARCH, GOROOT string
var installdir, androiddir string
var libSuffix, libgoname string

func TestMain(m *testing.M) {
	GOOS = goEnv("GOOS")
	GOARCH = goEnv("GOARCH")
	GOROOT = goEnv("GOROOT")

	if _, err := os.Stat(GOROOT); os.IsNotExist(err) {
		log.Fatalf("Unable able to find GOROOT at '%s'", GOROOT)
	}

	// Directory where cgo headers and outputs will be installed.
	// The installation directory format varies depending on the platform.
	installdir = path.Join("pkg", fmt.Sprintf("%s_%s_testcshared_shared", GOOS, GOARCH))
	switch GOOS {
	case "darwin":
		libSuffix = "dylib"
		installdir = path.Join("pkg", fmt.Sprintf("%s_%s_testcshared", GOOS, GOARCH))
	case "windows":
		libSuffix = "dll"
	default:
		libSuffix = "so"
	}

	androiddir = fmt.Sprintf("/data/local/tmp/testcshared-%d", os.Getpid())
	if GOOS == "android" {
		cmd := exec.Command("adb", "shell", "mkdir", "-p", androiddir)
		out, err := cmd.CombinedOutput()
		if err != nil {
			log.Fatalf("setupAndroid failed: %v\n%s\n", err, out)
		}
	}

	libgoname = "libgo." + libSuffix

	cc = []string{goEnv("CC")}

	out := goEnv("GOGCCFLAGS")
	quote := '\000'
	start := 0
	lastSpace := true
	backslash := false
	s := string(out)
	for i, c := range s {
		if quote == '\000' && unicode.IsSpace(c) {
			if !lastSpace {
				cc = append(cc, s[start:i])
				lastSpace = true
			}
		} else {
			if lastSpace {
				start = i
				lastSpace = false
			}
			if quote == '\000' && !backslash && (c == '"' || c == '\'') {
				quote = c
				backslash = false
			} else if !backslash && quote == c {
				quote = '\000'
			} else if (quote == '\000' || quote == '"') && !backslash && c == '\\' {
				backslash = true
			} else {
				backslash = false
			}
		}
	}
	if !lastSpace {
		cc = append(cc, s[start:])
	}

	switch GOOS {
	case "darwin":
		// For Darwin/ARM.
		// TODO(crawshaw): can we do better?
		cc = append(cc, []string{"-framework", "CoreFoundation", "-framework", "Foundation"}...)
	case "android":
		cc = append(cc, "-pie", "-fuse-ld=gold")
	}
	libgodir := GOOS + "_" + GOARCH
	switch GOOS {
	case "darwin":
		if GOARCH == "arm" || GOARCH == "arm64" {
			libgodir += "_shared"
		}
	case "dragonfly", "freebsd", "linux", "netbsd", "openbsd", "solaris":
		libgodir += "_shared"
	}
	cc = append(cc, "-I", filepath.Join("pkg", libgodir))

	// Build an environment with GOPATH=$(pwd)
	dir, err := os.Getwd()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(2)
	}
	gopathEnv = append(os.Environ(), "GOPATH="+dir)

	if GOOS == "windows" {
		exeSuffix = ".exe"
	}

	st := m.Run()

	os.Remove(libgoname)
	os.RemoveAll("pkg")
	cleanupHeaders()
	cleanupAndroid()

	os.Exit(st)
}

func goEnv(key string) string {
	out, err := exec.Command("go", "env", key).Output()
	if err != nil {
		fmt.Fprintf(os.Stderr, "go env %s failed:\n%s", key, err)
		fmt.Fprintf(os.Stderr, "%s", err.(*exec.ExitError).Stderr)
		os.Exit(2)
	}
	return strings.TrimSpace(string(out))
}

func cmdToRun(name string) string {
	return "./" + name + exeSuffix
}

func adbPush(t *testing.T, filename string) {
	if GOOS != "android" {
		return
	}
	args := []string{"adb", "push", filename, fmt.Sprintf("%s/%s", androiddir, filename)}
	cmd := exec.Command(args[0], args[1:]...)
	if out, err := cmd.CombinedOutput(); err != nil {
		t.Fatalf("adb command failed: %v\n%s\n", err, out)
	}
}

func adbRun(t *testing.T, env []string, adbargs ...string) string {
	if GOOS != "android" {
		t.Fatalf("trying to run adb command when operating system is not android.")
	}
	args := []string{"adb", "shell"}
	// Propagate LD_LIBRARY_PATH to the adb shell invocation.
	for _, e := range env {
		if strings.Index(e, "LD_LIBRARY_PATH=") != -1 {
			adbargs = append([]string{e}, adbargs...)
			break
		}
	}
	shellcmd := fmt.Sprintf("cd %s; %s", androiddir, strings.Join(adbargs, " "))
	args = append(args, shellcmd)
	cmd := exec.Command(args[0], args[1:]...)
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("adb command failed: %v\n%s\n", err, out)
	}
	return strings.Replace(string(out), "\r", "", -1)
}

func run(t *testing.T, env []string, args ...string) string {
	cmd := exec.Command(args[0], args[1:]...)
	cmd.Env = env
	out, err := cmd.CombinedOutput()
	if err != nil {
		t.Fatalf("command failed: %v\n%v\n%s\n", args, err, out)
	} else {
		t.Logf("run: %v", args)
	}
	return string(out)
}

func runExe(t *testing.T, env []string, args ...string) string {
	if GOOS == "android" {
		return adbRun(t, env, args...)
	}
	return run(t, env, args...)
}

func runCC(t *testing.T, args ...string) string {
	return run(t, nil, append(cc, args...)...)
}

func createHeaders() error {
	args := []string{"go", "install", "-buildmode=c-shared",
		"-installsuffix", "testcshared", "libgo"}
	cmd := exec.Command(args[0], args[1:]...)
	cmd.Env = gopathEnv
	out, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("command failed: %v\n%v\n%s\n", args, err, out)
	}

	args = []string{"go", "build", "-buildmode=c-shared",
		"-installsuffix", "testcshared",
		"-o", libgoname,
		filepath.Join("src", "libgo", "libgo.go")}
	cmd = exec.Command(args[0], args[1:]...)
	cmd.Env = gopathEnv
	out, err = cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("command failed: %v\n%v\n%s\n", args, err, out)
	}

	if GOOS == "android" {
		args = []string{"adb", "push", libgoname, fmt.Sprintf("%s/%s", androiddir, libgoname)}
		cmd = exec.Command(args[0], args[1:]...)
		out, err = cmd.CombinedOutput()
		if err != nil {
			return fmt.Errorf("adb command failed: %v\n%s\n", err, out)
		}
	}

	return nil
}

var (
	headersOnce sync.Once
	headersErr  error
)

func createHeadersOnce(t *testing.T) {
	headersOnce.Do(func() {
		headersErr = createHeaders()
	})
	if headersErr != nil {
		t.Fatal(headersErr)
	}
}

func cleanupHeaders() {
	os.Remove("libgo.h")
}

func cleanupAndroid() {
	if GOOS != "android" {
		return
	}
	cmd := exec.Command("adb", "shell", "rm", "-rf", androiddir)
	out, err := cmd.CombinedOutput()
	if err != nil {
		log.Fatalf("cleanupAndroid failed: %v\n%s\n", err, out)
	}
}

// test0: exported symbols in shared lib are accessible.
func TestExportedSymbols(t *testing.T) {
	t.Parallel()

	cmd := "testp0"

	createHeadersOnce(t)

	runCC(t, "-I", installdir, "-o", cmd, "main0.c", libgoname)
	adbPush(t, cmd)

	defer os.Remove(cmd)

	out := runExe(t, append(gopathEnv, "LD_LIBRARY_PATH=."), cmdToRun(cmd))
	if strings.TrimSpace(out) != "PASS" {
		t.Error(out)
	}
}

// test1: shared library can be dynamically loaded and exported symbols are accessible.
func TestExportedSymbolsWithDynamicLoad(t *testing.T) {
	t.Parallel()

	cmd := "testp1"

	createHeadersOnce(t)

	runCC(t, "-o", cmd, "main1.c", "-ldl")
	adbPush(t, cmd)

	defer os.Remove(cmd)

	out := runExe(t, nil, cmdToRun(cmd), "./"+libgoname)
	if strings.TrimSpace(out) != "PASS" {
		t.Error(out)
	}
}

// test2: tests libgo2 which does not export any functions.
func TestUnexportedSymbols(t *testing.T) {
	t.Parallel()

	cmd := "testp2"
	libname := "libgo2." + libSuffix

	run(t,
		gopathEnv,
		"go", "build",
		"-buildmode=c-shared",
		"-installsuffix", "testcshared",
		"-o", libname, "libgo2",
	)
	adbPush(t, libname)

	linkFlags := "-Wl,--no-as-needed"
	if GOOS == "darwin" {
		linkFlags = ""
	}

	runCC(t, "-o", cmd, "main2.c", linkFlags, libname)
	adbPush(t, cmd)

	defer os.Remove(libname)
	defer os.Remove(cmd)

	out := runExe(t, append(gopathEnv, "LD_LIBRARY_PATH=."), cmdToRun(cmd))

	if strings.TrimSpace(out) != "PASS" {
		t.Error(out)
	}
}

// test3: tests main.main is exported on android.
func TestMainExportedOnAndroid(t *testing.T) {
	t.Parallel()

	if GOOS != "android" {
		return
	}

	cmd := "testp3"

	createHeadersOnce(t)

	runCC(t, "-o", cmd, "main3.c", "-ldl")
	adbPush(t, cmd)

	defer os.Remove(cmd)

	out := runExe(t, nil, cmdToRun(cmd), "./"+libgoname)
	if strings.TrimSpace(out) != "PASS" {
		t.Error(out)
	}
}

func testSignalHandlers(t *testing.T, pkgname, cfile, cmd string) {
	libname := pkgname + "." + libSuffix
	run(t,
		gopathEnv,
		"go", "build",
		"-buildmode=c-shared",
		"-installsuffix", "testcshared",
		"-o", libname, pkgname,
	)
	adbPush(t, libname)
	runCC(t, "-pthread", "-o", cmd, cfile, "-ldl")
	adbPush(t, cmd)

	defer os.Remove(libname)
	defer os.Remove(cmd)
	defer os.Remove(pkgname + ".h")

	bin := cmdToRun(cmd)
	out := runExe(t, nil, bin, "./"+libname)
	if strings.TrimSpace(out) != "PASS" {
		t.Error(run(t, nil, bin, libname, "verbose"))
	}
}

// test4: test signal handlers
func TestSignalHandlers(t *testing.T) {
	t.Parallel()
	testSignalHandlers(t, "libgo4", "main4.c", "testp4")
}

// test5: test signal handlers with os/signal.Notify
func TestSignalHandlersWithNotify(t *testing.T) {
	t.Parallel()
	testSignalHandlers(t, "libgo5", "main5.c", "testp5")
}

func TestPIE(t *testing.T) {
	t.Parallel()

	switch GOOS {
	case "linux", "android":
		break
	default:
		t.Logf("Skipping TestPIE on %s", GOOS)
		return
	}

	createHeadersOnce(t)

	f, err := elf.Open(libgoname)
	if err != nil {
		t.Fatalf("elf.Open failed: %v", err)
	}
	defer f.Close()

	ds := f.SectionByType(elf.SHT_DYNAMIC)
	if ds == nil {
		t.Fatalf("no SHT_DYNAMIC section")
	}
	d, err := ds.Data()
	if err != nil {
		t.Fatalf("can't read SHT_DYNAMIC contents: %v", err)
	}
	for len(d) > 0 {
		var tag elf.DynTag
		switch f.Class {
		case elf.ELFCLASS32:
			tag = elf.DynTag(f.ByteOrder.Uint32(d[:4]))
			d = d[8:]
		case elf.ELFCLASS64:
			tag = elf.DynTag(f.ByteOrder.Uint64(d[:8]))
			d = d[16:]
		}
		if tag == elf.DT_TEXTREL {
			t.Fatalf("%s has DT_TEXTREL flag", libgoname)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Che Wei, Lin
//
// 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 tinynet

import (
	"errors"
	"net"
	"strconv"
	"time"

	"github.com/John-Lin/ovsdb"
	log "github.com/sirupsen/logrus"
)

// OVSSwitch is a bridge instance
type OVSSwitch struct {
	NodeType     string
	BridgeName   string
	CtrlHostPort string
	ovsdb        *ovsdb.OvsDriver
}

// NewOVSSwitch for creating a ovs bridge
func NewOVSSwitch(bridgeName string) (*OVSSwitch, error) {
	sw := new(OVSSwitch)
	sw.NodeType = "OVSSwitch"
	sw.BridgeName = bridgeName

	sw.ovsdb = ovsdb.NewOvsDriverWithUnix(bridgeName)
	log.Infoln("Adding a switch:", sw.BridgeName)

	// Check if port is already part of the OVS and add it
	if !sw.ovsdb.IsPortNamePresent(bridgeName) {
		// Create an internal port in OVS
		err := sw.ovsdb.CreatePort(bridgeName, "internal", 0)
		if err != nil {
			return nil, err
		}
	}

	time.Sleep(300 * time.Millisecond)
	// log.Infof("Waiting for OVS bridge %s setup", bridgeName)

	// ip link set ovs up
	_, err := ifaceUp(bridgeName)
	if err != nil {
		return nil, err
	}

	return sw, nil
}

// addPort for asking OVSDB driver to add the port
func (sw *OVSSwitch) addPort(ifName string) error {
	if !sw.ovsdb.IsPortNamePresent(ifName) {
		err := sw.ovsdb.CreatePort(ifName, "", 0)
		if err != nil {
			log.Fatalf("Error creating the port. Err: %v", err)
			return err
		}
	}
	return nil
}

// SetCtrl for seting up OpenFlow controller for ovs bridge
func (sw *OVSSwitch) SetCtrl(hostport string) error {
	host, port, err := net.SplitHostPort(hostport)
	if err != nil {
		log.Fatalf("Invalid controller IP and port. Err: %v", err)
		return err
	}
	uPort, err := strconv.ParseUint(port, 10, 32)
	if err != nil {
		log.Fatalf("Invalid controller port number. Err: %v", err)
		return err
	}
	err = sw.ovsdb.AddController(host, uint16(uPort))
	if err != nil {
		log.Fatalf("Error adding controller to OVS. Err: %v", err)
		return err
	}
	sw.CtrlHostPort = hostport
	return nil
}

func (sw *OVSSwitch) Delete() error {
	if exist := sw.ovsdb.IsBridgePresent(sw.BridgeName); exist != true {
		return errors.New(sw.BridgeName + " doesn't exist, we can delete")
	}

	return sw.ovsdb.Delete()
}
<commit_msg>call Delete Bridge, since Delete will disconnect<commit_after>// Copyright (c) 2017 Che Wei, Lin
//
// 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 tinynet

import (
	"errors"
	"net"
	"strconv"
	"time"

	"github.com/John-Lin/ovsdb"
	log "github.com/sirupsen/logrus"
)

// OVSSwitch is a bridge instance
type OVSSwitch struct {
	NodeType     string
	BridgeName   string
	CtrlHostPort string
	ovsdb        *ovsdb.OvsDriver
}

// NewOVSSwitch for creating a ovs bridge
func NewOVSSwitch(bridgeName string) (*OVSSwitch, error) {
	sw := new(OVSSwitch)
	sw.NodeType = "OVSSwitch"
	sw.BridgeName = bridgeName

	sw.ovsdb = ovsdb.NewOvsDriverWithUnix(bridgeName)
	log.Infoln("Adding a switch:", sw.BridgeName)

	// Check if port is already part of the OVS and add it
	if !sw.ovsdb.IsPortNamePresent(bridgeName) {
		// Create an internal port in OVS
		err := sw.ovsdb.CreatePort(bridgeName, "internal", 0)
		if err != nil {
			return nil, err
		}
	}

	time.Sleep(300 * time.Millisecond)
	// log.Infof("Waiting for OVS bridge %s setup", bridgeName)

	// ip link set ovs up
	_, err := ifaceUp(bridgeName)
	if err != nil {
		return nil, err
	}

	return sw, nil
}

// addPort for asking OVSDB driver to add the port
func (sw *OVSSwitch) addPort(ifName string) error {
	if !sw.ovsdb.IsPortNamePresent(ifName) {
		err := sw.ovsdb.CreatePort(ifName, "", 0)
		if err != nil {
			log.Fatalf("Error creating the port. Err: %v", err)
			return err
		}
	}
	return nil
}

// SetCtrl for seting up OpenFlow controller for ovs bridge
func (sw *OVSSwitch) SetCtrl(hostport string) error {
	host, port, err := net.SplitHostPort(hostport)
	if err != nil {
		log.Fatalf("Invalid controller IP and port. Err: %v", err)
		return err
	}
	uPort, err := strconv.ParseUint(port, 10, 32)
	if err != nil {
		log.Fatalf("Invalid controller port number. Err: %v", err)
		return err
	}
	err = sw.ovsdb.AddController(host, uint16(uPort))
	if err != nil {
		log.Fatalf("Error adding controller to OVS. Err: %v", err)
		return err
	}
	sw.CtrlHostPort = hostport
	return nil
}

func (sw *OVSSwitch) Delete() error {
	if exist := sw.ovsdb.IsBridgePresent(sw.BridgeName); exist != true {
		return errors.New(sw.BridgeName + " doesn't exist, we can delete")
	}

	return sw.ovsdb.DeleteBridge(sw.BridgeName)
}
<|endoftext|>


<commit_before>package goarmorapi

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

	"github.com/pkg/errors"
)

type JSONRequest struct {
	Payload interface{} `json:"payload,omitempty"`
	Time    uint64      `json:"time,omitempty"`
}

type JSONResponse struct {
	Success bool         `json:"success"`
	Errors  []*ErrorJSON `json:"messages,omitempty"`
	Payload interface{}  `json:"payload,omitempty"`
	Time    uint64       `json:"time,omitempty"`
}

type ErrorsJSON []*ErrorJSON

type ErrorJSON struct {
	Code     uint64 `json:"code"`
	Err      error  `json:"message,omitempty"`
	Public   bool   `json:"-"`
	Severity uint64 `json:"-"`
}

type ErrorJSONSeverity uint64

const (
	ErrSeverityUnknown ErrorJSONSeverity = iota
	ErrSeverityDebug
	ErrSeverityInfo
	ErrSeverityWarn
	ErrSeverityError
	ErrSeverityFatal
	ErrSeverityPanic
)

func (v ErrorJSONSeverity) Uint64() uint64 {
	return uint64(v)
}

func (v ErrorJSONSeverity) ErrorDefaultCode() uint64 {
	var u ErrorJSONCode

	switch v {
	default:
		return 0

	case ErrSeverityDebug:
		u = ErrCodeDefautlDebug

	case ErrSeverityInfo:
		u = ErrCodeDefautlInfo

	case ErrSeverityWarn:
		u = ErrCodeDefautlWarn

	case ErrSeverityError:
		u = ErrCodeDefautlError

	case ErrSeverityFatal:
		u = ErrCodeDefautlFatal

	case ErrSeverityPanic:
		u = ErrCodeDefautlPanic
	}

	return uint64(u)
}

type ErrorJSONCode uint64

const (
	ErrCodeDefautlDebug ErrorJSONCode = 1100
	ErrCodeDefautlInfo
	ErrCodeDefautlWarn

	ErrCodeDefautlError ErrorJSONCode = 5100
	ErrCodeDefautlFatal
	ErrCodeDefautlPanic
)

type ResponseErrorer interface {
	ResponseErrors() []*ErrorJSON
}

func (errorsJSON ErrorsJSON) First() error {
	a := []*ErrorJSON(errorsJSON)

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

	return a[0]
}

func (errorsJSON ErrorsJSON) Last() error {
	a := []*ErrorJSON(errorsJSON)

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

	return a[len(a)-1]
}

func (e *ErrorJSON) Error() string {
	return e.Err.Error()
}

func (e *ErrorJSON) MarshalJSON() ([]byte, error) {
	var (
		s string
		a []string
	)

	if e.Err != nil {
		s = e.Error()

		switch ErrorJSONSeverity(e.Severity) {
		case ErrSeverityError, ErrSeverityFatal, ErrSeverityPanic:
			a = strings.Split(fmt.Sprintf("%+v", e.Err), "\n")
			a = append(a[1:2], a[2:]...)
		}
	}

	return json.Marshal(&struct {
		Code       uint64   `json:"code,omitempty"`
		Message    string   `json:"message,omitempty"`
		StackTrace []string `json:"stackTrace,omitempty"`
	}{
		Code:       e.Code,
		Message:    s,
		StackTrace: a})
}

func (e *ErrorJSON) UnmarshalJSON(b []byte) error {
	s := &struct {
		Code    uint64 `json:"code"`
		Message string `json:"message"`
	}{}

	if err := json.Unmarshal(b, &s); err != nil {
		return err
	}

	e.Code = s.Code

	if s.Message != "" {
		e.Err = errors.New(s.Message)
	}

	return nil
}

func (j *JSONResponse) KV() (KV, error) {
	if j == nil {
		return nil, errors.New("empty api response")
	}

	if len(j.Errors) == 0 {
		return nil, errors.New("empty key values")
	}

	kv := NewKV()

	for _, e := range j.Errors {
		if e.Code != uint64(ErrCodeDefautlDebug) {
			continue
		}

		if e.Error() == "" {
			return nil, errors.New("empty kv")
		}

		x := strings.SplitN(e.Error(), ":", 2)
		if len(x) != 2 {
			return nil, errors.New("bad kv format")
		}

		kv[x[0]] = x[1]
	}

	if len(kv) == 0 {
		return nil, errors.New("empty kv")
	}

	return kv, nil
}

func NewJSONRequest(
	responsePayload interface{}) (*JSONRequest, error) {
	return &JSONRequest{
		Payload: responsePayload,
		Time:    uint64(time.Now().Unix())}, nil
}

func NewJSONResponse(
	debugLevel int,
	isSuccess bool,
	responsePayload interface{},
	responseErrorer ResponseErrorer,
	errs ...*ErrorJSON) (*JSONResponse, error) {
	publicErrors, err :=
		newJSONResponseErrors(debugLevel, responseErrorer, errs...)
	if err != nil {
		return nil, err
	}

	return &JSONResponse{
		Success: isSuccess,
		Errors:  publicErrors,
		Payload: responsePayload,
		Time:    uint64(time.Now().Unix())}, nil
}

func newJSONResponseErrors(
	debugLevel int,
	responseErrorer ResponseErrorer,
	errs ...*ErrorJSON) ([]*ErrorJSON, error) {
	errs = append(errs, responseErrorer.ResponseErrors()...)

	var publicErrors []*ErrorJSON

	if debugLevel > 0 {
		for _, x := range errs {
			publicErrors = append(publicErrors,
				&ErrorJSON{
					Code:     x.Code,
					Err:      errors.New(x.Error()),
					Public:   x.Public,
					Severity: x.Severity})
		}

	} else {
		isKVRemoved := false

		for _, x := range errs {
			if x.Public {
				publicErrors = append(publicErrors,
					&ErrorJSON{
						Code:     x.Code,
						Err:      errors.New(x.Error()),
						Public:   x.Public,
						Severity: x.Severity})

				continue
			}

			if x.Code == uint64(ErrCodeDefautlDebug) {
				isKVRemoved = true

				continue
			}

			publicErrors = append(publicErrors,
				&ErrorJSON{Code: x.Code, Severity: x.Severity})
		}

		if isKVRemoved {
			// Add empty (only with "code") "ErrorJSON" structure in order to be able to
			// determine was an key-values in hadler's response.
			publicErrors = append(publicErrors, &ErrorJSON{Code: uint64(ErrCodeDefautlDebug)})
		}
	}

	return publicErrors, nil
}
<commit_msg>minor<commit_after>package goarmorapi

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

	"github.com/pkg/errors"
)

type JSONRequest struct {
	Payload interface{} `json:"payload,omitempty"`
	Time    uint64      `json:"time,omitempty"`
}

type JSONResponse struct {
	Success bool         `json:"success"`
	Errors  []*ErrorJSON `json:"messages,omitempty"`
	Payload interface{}  `json:"payload,omitempty"`
	Time    uint64       `json:"time,omitempty"`
}

type ErrorsJSON []*ErrorJSON

type ErrorJSON struct {
	Code     uint64 `json:"code"`
	Err      error  `json:"message,omitempty"`
	Public   bool   `json:"-"`
	Severity uint64 `json:"-"`
}

type ErrorJSONSeverity uint64

const (
	ErrSeverityUnknown ErrorJSONSeverity = iota
	ErrSeverityDebug
	ErrSeverityInfo
	ErrSeverityWarn
	ErrSeverityError
	ErrSeverityFatal
	ErrSeverityPanic
)

func (v ErrorJSONSeverity) Uint64() uint64 {
	return uint64(v)
}

func (v ErrorJSONSeverity) ErrorDefaultCode() uint64 {
	var u ErrorJSONCode

	switch v {
	default:
		return 0

	case ErrSeverityDebug:
		u = ErrCodeDefautlDebug

	case ErrSeverityInfo:
		u = ErrCodeDefautlInfo

	case ErrSeverityWarn:
		u = ErrCodeDefautlWarn

	case ErrSeverityError:
		u = ErrCodeDefautlError

	case ErrSeverityFatal:
		u = ErrCodeDefautlFatal

	case ErrSeverityPanic:
		u = ErrCodeDefautlPanic
	}

	return uint64(u)
}

type ErrorJSONCode uint64

const (
	ErrCodeDefautlDebug ErrorJSONCode = 1100
	ErrCodeDefautlInfo
	ErrCodeDefautlWarn

	ErrCodeDefautlError ErrorJSONCode = 5100
	ErrCodeDefautlFatal
	ErrCodeDefautlPanic
)

type ResponseErrorer interface {
	ResponseErrors() []*ErrorJSON
}

func (errorsJSON ErrorsJSON) Errors() []error {
	var a []error
	for _, e := range errorsJSON {
		a = append(a, error(e))
	}
	return a
}

func (errorsJSON ErrorsJSON) First() error {
	a := []*ErrorJSON(errorsJSON)

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

	return a[0]
}

func (errorsJSON ErrorsJSON) Last() error {
	a := []*ErrorJSON(errorsJSON)

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

	return a[len(a)-1]
}

func (e *ErrorJSON) Error() string {
	return e.Err.Error()
}

func (e *ErrorJSON) MarshalJSON() ([]byte, error) {
	var (
		s string
		a []string
	)

	if e.Err != nil {
		s = e.Error()

		switch ErrorJSONSeverity(e.Severity) {
		case ErrSeverityError, ErrSeverityFatal, ErrSeverityPanic:
			a = strings.Split(fmt.Sprintf("%+v", e.Err), "\n")
			a = append(a[1:2], a[2:]...)
		}
	}

	return json.Marshal(&struct {
		Code       uint64   `json:"code,omitempty"`
		Message    string   `json:"message,omitempty"`
		StackTrace []string `json:"stackTrace,omitempty"`
	}{
		Code:       e.Code,
		Message:    s,
		StackTrace: a})
}

func (e *ErrorJSON) UnmarshalJSON(b []byte) error {
	s := &struct {
		Code    uint64 `json:"code"`
		Message string `json:"message"`
	}{}

	if err := json.Unmarshal(b, &s); err != nil {
		return err
	}

	e.Code = s.Code

	if s.Message != "" {
		e.Err = errors.New(s.Message)
	}

	return nil
}

func (j *JSONResponse) KV() (KV, error) {
	if j == nil {
		return nil, errors.New("empty api response")
	}

	if len(j.Errors) == 0 {
		return nil, errors.New("empty key values")
	}

	kv := NewKV()

	for _, e := range j.Errors {
		if e.Code != uint64(ErrCodeDefautlDebug) {
			continue
		}

		if e.Error() == "" {
			return nil, errors.New("empty kv")
		}

		x := strings.SplitN(e.Error(), ":", 2)
		if len(x) != 2 {
			return nil, errors.New("bad kv format")
		}

		kv[x[0]] = x[1]
	}

	if len(kv) == 0 {
		return nil, errors.New("empty kv")
	}

	return kv, nil
}

func NewJSONRequest(
	responsePayload interface{}) (*JSONRequest, error) {
	return &JSONRequest{
		Payload: responsePayload,
		Time:    uint64(time.Now().Unix())}, nil
}

func NewJSONResponse(
	debugLevel int,
	isSuccess bool,
	responsePayload interface{},
	responseErrorer ResponseErrorer,
	errs ...*ErrorJSON) (*JSONResponse, error) {
	publicErrors, err :=
		newJSONResponseErrors(debugLevel, responseErrorer, errs...)
	if err != nil {
		return nil, err
	}

	return &JSONResponse{
		Success: isSuccess,
		Errors:  publicErrors,
		Payload: responsePayload,
		Time:    uint64(time.Now().Unix())}, nil
}

func newJSONResponseErrors(
	debugLevel int,
	responseErrorer ResponseErrorer,
	errs ...*ErrorJSON) ([]*ErrorJSON, error) {
	errs = append(errs, responseErrorer.ResponseErrors()...)

	var publicErrors []*ErrorJSON

	if debugLevel > 0 {
		for _, x := range errs {
			publicErrors = append(publicErrors,
				&ErrorJSON{
					Code:     x.Code,
					Err:      errors.New(x.Error()),
					Public:   x.Public,
					Severity: x.Severity})
		}

	} else {
		isKVRemoved := false

		for _, x := range errs {
			if x.Public {
				publicErrors = append(publicErrors,
					&ErrorJSON{
						Code:     x.Code,
						Err:      errors.New(x.Error()),
						Public:   x.Public,
						Severity: x.Severity})

				continue
			}

			if x.Code == uint64(ErrCodeDefautlDebug) {
				isKVRemoved = true

				continue
			}

			publicErrors = append(publicErrors,
				&ErrorJSON{Code: x.Code, Severity: x.Severity})
		}

		if isKVRemoved {
			// Add empty (only with "code") "ErrorJSON" structure in order to be able to
			// determine was an key-values in hadler's response.
			publicErrors = append(publicErrors, &ErrorJSON{Code: uint64(ErrCodeDefautlDebug)})
		}
	}

	return publicErrors, nil
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ssm"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsSsmAssociation() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsSsmAssociationCreate,
		Read:   resourceAwsSsmAssociationRead,
		Update: resourceAwsSsmAssociationUpdate,
		Delete: resourceAwsSsmAssociationDelete,

		MigrateState:  resourceAwsSsmAssociationMigrateState,
		SchemaVersion: 1,

		Schema: map[string]*schema.Schema{
			"association_name": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"association_id": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"instance_id": {
				Type:     schema.TypeString,
				ForceNew: true,
				Optional: true,
			},
			"document_version": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"name": {
				Type:     schema.TypeString,
				ForceNew: true,
				Required: true,
			},
			"parameters": {
				Type:     schema.TypeMap,
				Optional: true,
				Computed: true,
			},
			"schedule_expression": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"output_location": {
				Type:     schema.TypeList,
				MaxItems: 1,
				Optional: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"s3_bucket_name": {
							Type:     schema.TypeString,
							Required: true,
						},
						"s3_key_prefix": {
							Type:     schema.TypeString,
							Optional: true,
						},
					},
				},
			},
			"targets": {
				Type:     schema.TypeList,
				Optional: true,
				Computed: true,
				MaxItems: 5,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"key": {
							Type:     schema.TypeString,
							Required: true,
						},
						"values": {
							Type:     schema.TypeList,
							Required: true,
							Elem:     &schema.Schema{Type: schema.TypeString},
						},
					},
				},
			},
		},
	}
}

func resourceAwsSsmAssociationCreate(d *schema.ResourceData, meta interface{}) error {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[DEBUG] SSM association create: %s", d.Id())

	associationInput := &ssm.CreateAssociationInput{
		Name: aws.String(d.Get("name").(string)),
	}

	if v, ok := d.GetOk("association_name"); ok {
		associationInput.AssociationName = aws.String(v.(string))
	}

	if v, ok := d.GetOk("instance_id"); ok {
		associationInput.InstanceId = aws.String(v.(string))
	}

	if v, ok := d.GetOk("document_version"); ok {
		associationInput.DocumentVersion = aws.String(v.(string))
	}

	if v, ok := d.GetOk("schedule_expression"); ok {
		associationInput.ScheduleExpression = aws.String(v.(string))
	}

	if v, ok := d.GetOk("parameters"); ok {
		associationInput.Parameters = expandSSMDocumentParameters(v.(map[string]interface{}))
	}

	if _, ok := d.GetOk("targets"); ok {
		associationInput.Targets = expandAwsSsmTargets(d.Get("targets").([]interface{}))
	}

	if v, ok := d.GetOk("output_location"); ok {
		associationInput.OutputLocation = expandSSMAssociationOutputLocation(v.([]interface{}))
	}

	resp, err := ssmconn.CreateAssociation(associationInput)
	if err != nil {
		return fmt.Errorf("[ERROR] Error creating SSM association: %s", err)
	}

	if resp.AssociationDescription == nil {
		return fmt.Errorf("[ERROR] AssociationDescription was nil")
	}

	d.SetId(*resp.AssociationDescription.AssociationId)
	d.Set("association_id", resp.AssociationDescription.AssociationId)

	return resourceAwsSsmAssociationRead(d, meta)
}

func resourceAwsSsmAssociationRead(d *schema.ResourceData, meta interface{}) error {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[DEBUG] Reading SSM Association: %s", d.Id())

	params := &ssm.DescribeAssociationInput{
		AssociationId: aws.String(d.Id()),
	}

	resp, err := ssmconn.DescribeAssociation(params)

	if err != nil {
		if isAWSErr(err, ssm.ErrCodeAssociationDoesNotExist, "") {
			d.SetId("")
			return nil
		}
		return fmt.Errorf("[ERROR] Error reading SSM association: %s", err)
	}
	if resp.AssociationDescription == nil {
		return fmt.Errorf("[ERROR] AssociationDescription was nil")
	}

	association := resp.AssociationDescription
	d.Set("association_name", association.AssociationName)
	d.Set("instance_id", association.InstanceId)
	d.Set("name", association.Name)
	d.Set("parameters", association.Parameters)
	d.Set("association_id", association.AssociationId)
	d.Set("schedule_expression", association.ScheduleExpression)
	d.Set("document_version", association.DocumentVersion)

	if err := d.Set("targets", flattenAwsSsmTargets(association.Targets)); err != nil {
		return fmt.Errorf("[DEBUG] Error setting targets error: %#v", err)
	}

	if err := d.Set("output_location", flattenAwsSsmAssociationOutoutLocation(association.OutputLocation)); err != nil {
		return fmt.Errorf("[DEBUG] Error setting output_location error: %#v", err)
	}

	return nil
}

func resourceAwsSsmAssociationUpdate(d *schema.ResourceData, meta interface{}) error {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[DEBUG] SSM Association update: %s", d.Id())

	associationInput := &ssm.UpdateAssociationInput{
		AssociationId: aws.String(d.Get("association_id").(string)),
	}

	// AWS creates a new version every time the association is updated, so everything should be passed in the update.

	hasChanges := false

	if d.HasChange("association_name") {
		hasChanges = true
	}

	if d.HasChange("document_version") {
		hasChanges = true
	}

	if d.HasChange("schedule_expression") {
		hasChanges = true
	}

	if d.HasChange("parameters") {
		hasChanges = true
	}

	if d.HasChange("output_location") {
		hasChanges = true
	}

	if d.HasChange("targets") {
		hasChanges = true
	}

	if hasChanges {
		if v, ok := d.GetOk("association_name"); ok {
			associationInput.AssociationName = aws.String(v.(string))
		}

		if v, ok := d.GetOk("document_version"); ok {
			associationInput.DocumentVersion = aws.String(v.(string))
		}

		if v, ok := d.GetOk("schedule_expression"); ok {
			associationInput.ScheduleExpression = aws.String(v.(string))
		}

		if v, ok := d.GetOk("parameters"); ok {
			associationInput.Parameters = expandSSMDocumentParameters(v.(map[string]interface{}))
		}

		if _, ok := d.GetOk("targets"); ok {
			associationInput.Targets = expandAwsSsmTargets(d.Get("targets").([]interface{}))
		}

		if v, ok := d.GetOk("output_location"); ok {
			associationInput.OutputLocation = expandSSMAssociationOutputLocation(v.([]interface{}))
		}
	}

	_, err := ssmconn.UpdateAssociation(associationInput)
	if err != nil {
		return fmt.Errorf("[ERROR] Error updating SSM association: %s", err)
	}

	return resourceAwsSsmAssociationRead(d, meta)
}

func resourceAwsSsmAssociationDelete(d *schema.ResourceData, meta interface{}) error {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[DEBUG] Deleting SSM Association: %s", d.Id())

	params := &ssm.DeleteAssociationInput{
		AssociationId: aws.String(d.Get("association_id").(string)),
	}

	_, err := ssmconn.DeleteAssociation(params)

	if err != nil {
		return fmt.Errorf("[ERROR] Error deleting SSM association: %s", err)
	}

	return nil
}

func expandSSMDocumentParameters(params map[string]interface{}) map[string][]*string {
	var docParams = make(map[string][]*string)
	for k, v := range params {
		values := make([]*string, 1)
		values[0] = aws.String(v.(string))
		docParams[k] = values
	}

	return docParams
}

func expandSSMAssociationOutputLocation(config []interface{}) *ssm.InstanceAssociationOutputLocation {
	if config == nil {
		return nil
	}

	//We only allow 1 Item so we can grab the first in the list only
	locationConfig := config[0].(map[string]interface{})

	S3OutputLocation := &ssm.S3OutputLocation{
		OutputS3BucketName: aws.String(locationConfig["s3_bucket_name"].(string)),
	}

	if v, ok := locationConfig["s3_key_prefix"]; ok {
		S3OutputLocation.OutputS3KeyPrefix = aws.String(v.(string))
	}

	return &ssm.InstanceAssociationOutputLocation{
		S3Location: S3OutputLocation,
	}
}

func flattenAwsSsmAssociationOutoutLocation(location *ssm.InstanceAssociationOutputLocation) []map[string]interface{} {
	if location == nil {
		return nil
	}

	result := make([]map[string]interface{}, 0)
	item := make(map[string]interface{})

	item["s3_bucket_name"] = *location.S3Location.OutputS3BucketName

	if location.S3Location.OutputS3KeyPrefix != nil {
		item["s3_key_prefix"] = *location.S3Location.OutputS3KeyPrefix
	}

	result = append(result, item)

	return result
}
<commit_msg>resource/aws_ssm_association: Refactor out extraneous hasChanges logic in update function<commit_after>package aws

import (
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ssm"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsSsmAssociation() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsSsmAssociationCreate,
		Read:   resourceAwsSsmAssociationRead,
		Update: resourceAwsSsmAssociationUpdate,
		Delete: resourceAwsSsmAssociationDelete,

		MigrateState:  resourceAwsSsmAssociationMigrateState,
		SchemaVersion: 1,

		Schema: map[string]*schema.Schema{
			"association_name": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"association_id": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"instance_id": {
				Type:     schema.TypeString,
				ForceNew: true,
				Optional: true,
			},
			"document_version": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"name": {
				Type:     schema.TypeString,
				ForceNew: true,
				Required: true,
			},
			"parameters": {
				Type:     schema.TypeMap,
				Optional: true,
				Computed: true,
			},
			"schedule_expression": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"output_location": {
				Type:     schema.TypeList,
				MaxItems: 1,
				Optional: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"s3_bucket_name": {
							Type:     schema.TypeString,
							Required: true,
						},
						"s3_key_prefix": {
							Type:     schema.TypeString,
							Optional: true,
						},
					},
				},
			},
			"targets": {
				Type:     schema.TypeList,
				Optional: true,
				Computed: true,
				MaxItems: 5,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"key": {
							Type:     schema.TypeString,
							Required: true,
						},
						"values": {
							Type:     schema.TypeList,
							Required: true,
							Elem:     &schema.Schema{Type: schema.TypeString},
						},
					},
				},
			},
		},
	}
}

func resourceAwsSsmAssociationCreate(d *schema.ResourceData, meta interface{}) error {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[DEBUG] SSM association create: %s", d.Id())

	associationInput := &ssm.CreateAssociationInput{
		Name: aws.String(d.Get("name").(string)),
	}

	if v, ok := d.GetOk("association_name"); ok {
		associationInput.AssociationName = aws.String(v.(string))
	}

	if v, ok := d.GetOk("instance_id"); ok {
		associationInput.InstanceId = aws.String(v.(string))
	}

	if v, ok := d.GetOk("document_version"); ok {
		associationInput.DocumentVersion = aws.String(v.(string))
	}

	if v, ok := d.GetOk("schedule_expression"); ok {
		associationInput.ScheduleExpression = aws.String(v.(string))
	}

	if v, ok := d.GetOk("parameters"); ok {
		associationInput.Parameters = expandSSMDocumentParameters(v.(map[string]interface{}))
	}

	if _, ok := d.GetOk("targets"); ok {
		associationInput.Targets = expandAwsSsmTargets(d.Get("targets").([]interface{}))
	}

	if v, ok := d.GetOk("output_location"); ok {
		associationInput.OutputLocation = expandSSMAssociationOutputLocation(v.([]interface{}))
	}

	resp, err := ssmconn.CreateAssociation(associationInput)
	if err != nil {
		return fmt.Errorf("[ERROR] Error creating SSM association: %s", err)
	}

	if resp.AssociationDescription == nil {
		return fmt.Errorf("[ERROR] AssociationDescription was nil")
	}

	d.SetId(*resp.AssociationDescription.AssociationId)
	d.Set("association_id", resp.AssociationDescription.AssociationId)

	return resourceAwsSsmAssociationRead(d, meta)
}

func resourceAwsSsmAssociationRead(d *schema.ResourceData, meta interface{}) error {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[DEBUG] Reading SSM Association: %s", d.Id())

	params := &ssm.DescribeAssociationInput{
		AssociationId: aws.String(d.Id()),
	}

	resp, err := ssmconn.DescribeAssociation(params)

	if err != nil {
		if isAWSErr(err, ssm.ErrCodeAssociationDoesNotExist, "") {
			d.SetId("")
			return nil
		}
		return fmt.Errorf("[ERROR] Error reading SSM association: %s", err)
	}
	if resp.AssociationDescription == nil {
		return fmt.Errorf("[ERROR] AssociationDescription was nil")
	}

	association := resp.AssociationDescription
	d.Set("association_name", association.AssociationName)
	d.Set("instance_id", association.InstanceId)
	d.Set("name", association.Name)
	d.Set("parameters", association.Parameters)
	d.Set("association_id", association.AssociationId)
	d.Set("schedule_expression", association.ScheduleExpression)
	d.Set("document_version", association.DocumentVersion)

	if err := d.Set("targets", flattenAwsSsmTargets(association.Targets)); err != nil {
		return fmt.Errorf("[DEBUG] Error setting targets error: %#v", err)
	}

	if err := d.Set("output_location", flattenAwsSsmAssociationOutoutLocation(association.OutputLocation)); err != nil {
		return fmt.Errorf("[DEBUG] Error setting output_location error: %#v", err)
	}

	return nil
}

func resourceAwsSsmAssociationUpdate(d *schema.ResourceData, meta interface{}) error {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[DEBUG] SSM Association update: %s", d.Id())

	associationInput := &ssm.UpdateAssociationInput{
		AssociationId: aws.String(d.Get("association_id").(string)),
	}

	// AWS creates a new version every time the association is updated, so everything should be passed in the update.
	if v, ok := d.GetOk("association_name"); ok {
		associationInput.AssociationName = aws.String(v.(string))
	}

	if v, ok := d.GetOk("document_version"); ok {
		associationInput.DocumentVersion = aws.String(v.(string))
	}

	if v, ok := d.GetOk("schedule_expression"); ok {
		associationInput.ScheduleExpression = aws.String(v.(string))
	}

	if v, ok := d.GetOk("parameters"); ok {
		associationInput.Parameters = expandSSMDocumentParameters(v.(map[string]interface{}))
	}

	if _, ok := d.GetOk("targets"); ok {
		associationInput.Targets = expandAwsSsmTargets(d.Get("targets").([]interface{}))
	}

	if v, ok := d.GetOk("output_location"); ok {
		associationInput.OutputLocation = expandSSMAssociationOutputLocation(v.([]interface{}))
	}

	_, err := ssmconn.UpdateAssociation(associationInput)
	if err != nil {
		return fmt.Errorf("[ERROR] Error updating SSM association: %s", err)
	}

	return resourceAwsSsmAssociationRead(d, meta)
}

func resourceAwsSsmAssociationDelete(d *schema.ResourceData, meta interface{}) error {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[DEBUG] Deleting SSM Association: %s", d.Id())

	params := &ssm.DeleteAssociationInput{
		AssociationId: aws.String(d.Get("association_id").(string)),
	}

	_, err := ssmconn.DeleteAssociation(params)

	if err != nil {
		return fmt.Errorf("[ERROR] Error deleting SSM association: %s", err)
	}

	return nil
}

func expandSSMDocumentParameters(params map[string]interface{}) map[string][]*string {
	var docParams = make(map[string][]*string)
	for k, v := range params {
		values := make([]*string, 1)
		values[0] = aws.String(v.(string))
		docParams[k] = values
	}

	return docParams
}

func expandSSMAssociationOutputLocation(config []interface{}) *ssm.InstanceAssociationOutputLocation {
	if config == nil {
		return nil
	}

	//We only allow 1 Item so we can grab the first in the list only
	locationConfig := config[0].(map[string]interface{})

	S3OutputLocation := &ssm.S3OutputLocation{
		OutputS3BucketName: aws.String(locationConfig["s3_bucket_name"].(string)),
	}

	if v, ok := locationConfig["s3_key_prefix"]; ok {
		S3OutputLocation.OutputS3KeyPrefix = aws.String(v.(string))
	}

	return &ssm.InstanceAssociationOutputLocation{
		S3Location: S3OutputLocation,
	}
}

func flattenAwsSsmAssociationOutoutLocation(location *ssm.InstanceAssociationOutputLocation) []map[string]interface{} {
	if location == nil {
		return nil
	}

	result := make([]map[string]interface{}, 0)
	item := make(map[string]interface{})

	item["s3_bucket_name"] = *location.S3Location.OutputS3BucketName

	if location.S3Location.OutputS3KeyPrefix != nil {
		item["s3_key_prefix"] = *location.S3Location.OutputS3KeyPrefix
	}

	result = append(result, item)

	return result
}
<|endoftext|>