<commit_before>package dsdk

import (
	"context"
	_path "path"

	greq "github.com/levigross/grequests"
)

const (
	ProviderAWS    = "AWS S3"
	ProviderGoogle = "Google Cloud"
	ProviderS3     = "S3 Object Store"
)

type RemoteProvider struct {
	Path              string                   `json:"path,omitempty" mapstructure:"path"`
	Uuid              string                   `json:"uuid,omitempty" mapstructure:"uuid"`
	AccountId         string                   `json:"account_id,omitempty" mapstructure:"account_id"`
	RemoteType        string                   `json:"remote_type,omitempty" mapstructure:"remote_type"`
	LastSeenTimestamp string                   `json:"last_seen_timestamp,omitempty" mapstructure:"last_seen_timestamp"`
	Operations        []map[string]interface{} `json:"operations,omitempty" mapstructure:"operations"`
	Snapshots         []*Snapshot              `json:"snapshots,omitempty" mapstructure:"snapshots"`
	Label             string                   `json:"label,omitempty" mapstructure:"label"`
	Status            string                   `json:"status,omitempty" mapstructure:"status"`
	Host              string                   `json:"host,omitempty" mapstructure:"host"`
	Port              string                   `json:"port,omitempty" mapstructure:"port"`
	OperationsEp      string
	SnapshotsEp       *Snapshots
}

func RegisterRemoteProviderEndpoints(rp *RemoteProvider) {
	//a.OperationsEp = newOperations(a.Path)
	rp.SnapshotsEp = newSnapshots(rp.Path)
}

type RemoteProviders struct {
	Path string
}

type RemoteProvidersCreateRequest struct {
	Ctxt        context.Context `json:"-"`
	ProjectName string          `json:"project_name,omitempty" mapstructure:"project_name"`
	AccountId   string          `json:"account_id,omitempty" mapstructure:"account_id"`
	RemoteType  string          `json:"remote_type,omitempty" mapstructure:"remote_type"`
	PrivateKey  string          `json:"private_key,omitempty" mapstructure:"private_key"`
	Label       string          `json:"label,omitempty" mapstructure:"label"`
	Host        string          `json:"host,omitempty" mapstructure:"host"`
	Port        int             `json:"port,omitempty" mapstructure:"port"`
	AccessKey   string          `json:"access_key,omitempty" mapstructure:"access_key"`
	SecretKey   string          `json:"secret_key,omitempty" mapstructure:"secret_key"`
}

func newRemoteProviders(path string) *RemoteProviders {
	return &RemoteProviders{
		Path: _path.Join(path, "remote_providers"),
	}
}

func (e *RemoteProviders) Create(ro *RemoteProvidersCreateRequest) (*RemoteProvider, *ApiErrorResponse, error) {
	gro := &greq.RequestOptions{JSON: ro}
	rs, apierr, err := GetConn(ro.Ctxt).Post(ro.Ctxt, e.Path, gro)
	if apierr != nil {
		return nil, apierr, err
	}
	if err != nil {
		return nil, nil, err
	}
	resp := &RemoteProvider{}
	if err = FillStruct(rs.Data, resp); err != nil {
		return nil, nil, err
	}
	RegisterRemoteProviderEndpoints(resp)
	return resp, nil, nil
}

type RemoteProvidersListRequest struct {
	Ctxt   context.Context `json:"-"`
	Params ListParams      `json:"params,omitempty"`
}

func (e *RemoteProviders) List(ro *RemoteProvidersListRequest) ([]*RemoteProvider, *ApiErrorResponse, error) {
	gro := &greq.RequestOptions{
		JSON:   ro,
		Params: ro.Params.ToMap()}
	rs, apierr, err := GetConn(ro.Ctxt).GetList(ro.Ctxt, e.Path, gro)
	if apierr != nil {
		return nil, apierr, err
	}
	if err != nil {
		return nil, nil, err
	}
	resp := []*RemoteProvider{}
	for _, data := range rs.Data {
		elem := &RemoteProvider{}
		adata := data.(map[string]interface{})
		if err = FillStruct(adata, elem); err != nil {
			return nil, nil, err
		}
		RegisterRemoteProviderEndpoints(elem)
		resp = append(resp, elem)
	}
	return resp, nil, nil
}

type RemoteProvidersGetRequest struct {
	Ctxt context.Context `json:"-"`
	Id   string          `json:"-"`
}

func (e *RemoteProviders) Get(ro *RemoteProvidersGetRequest) (*RemoteProvider, *ApiErrorResponse, error) {
	gro := &greq.RequestOptions{JSON: ro}
	rs, apierr, err := GetConn(ro.Ctxt).Get(ro.Ctxt, _path.Join(e.Path, ro.Id), gro)
	if apierr != nil {
		return nil, apierr, err
	}
	if err != nil {
		return nil, nil, err
	}
	resp := &RemoteProvider{}
	if err = FillStruct(rs.Data, resp); err != nil {
		return nil, nil, err
	}
	RegisterRemoteProviderEndpoints(resp)
	return resp, nil, nil
}

type RemoteProvidersRefreshRequest struct {
	Ctxt context.Context `json:"-"`
	Uuid string          `json:"-"`
}

type RemoteProvidersRefreshResponse struct {
	Uuid string `json:"uuid,omitempty" mapstructure:"uuid"`
}

func (e *RemoteProviders) Refresh(ro *RemoteProvidersRefreshRequest) (*RemoteProvidersRefreshResponse, *ApiErrorResponse, error) {
	gro := &greq.RequestOptions{JSON: ro}
	rs, apierr, err := GetConn(ro.Ctxt).Put(ro.Ctxt, _path.Join(e.Path, ro.Uuid, "refresh"), gro)

	if apierr != nil {
		return nil, apierr, err
	}
	if err != nil {
		return nil, nil, err
	}

	resp := &RemoteProvidersRefreshResponse{}
	if err = FillStruct(rs.Data, resp); err != nil {
		return nil, nil, err
	}

	return resp, nil, nil
}

type RemoteProviderSetRequest struct {
	Ctxt        context.Context `json:"-"`
	ProjectName string          `json:"project_name,omitempty" mapstructure:"project_name"`
	AccountId   string          `json:"account_id,omitempty" mapstructure:"account_id"`
	PrivateKey  string          `json:"private_key,omitempty" mapstructure:"private_key"`
	Label       string          `json:"label,omitempty" mapstructure:"label"`
	Host        string          `json:"host,omitempty" mapstructure:"host"`
	Port        int             `json:"port,omitempty" mapstructure:"port"`
	AccessKey   string          `json:"access_key,omitempty" mapstructure:"access_key"`
	SecretKey   string          `json:"secret_key,omitempty" mapstructure:"secret_key"`
}

func (e *RemoteProvider) Set(ro *RemoteProviderSetRequest) (*RemoteProvider, *ApiErrorResponse, error) {
	gro := &greq.RequestOptions{JSON: ro}
	rs, apierr, err := GetConn(ro.Ctxt).Put(ro.Ctxt, e.Path, gro)
	if apierr != nil {
		return nil, apierr, err
	}
	if err != nil {
		return nil, nil, err
	}
	resp := &RemoteProvider{}
	if err = FillStruct(rs.Data, resp); err != nil {
		return nil, nil, err
	}
	RegisterRemoteProviderEndpoints(resp)
	return resp, nil, nil
}

type RemoteProviderDeleteRequest struct {
	Ctxt  context.Context `json:"-"`
	Force bool            `json:"force,omitempty" mapstructure:"force"`
}

func (e *RemoteProvider) Delete(ro *RemoteProviderDeleteRequest) (*RemoteProvider, *ApiErrorResponse, error) {
	rs, apierr, err := GetConn(ro.Ctxt).Delete(ro.Ctxt, e.Path, nil)
	if apierr != nil {
		return nil, apierr, err
	}
	if err != nil {
		return nil, nil, err
	}
	resp := &RemoteProvider{}
	if err = FillStruct(rs.Data, resp); err != nil {
		return nil, nil, err
	}
	RegisterRemoteProviderEndpoints(resp)
	return resp, nil, nil
}

type RemoteProviderAppTemplate struct {
	Path           string `json:"path,omitempty" mapstructure:"path"`
	ResolvedPath   string `json:"resolved_path,omitempty" mapstructure:"resolved_path"`
	ResolvedTenant string `json:"resolved_tenant,omitempty" mapstructure:"resolved_tenant"`
}

type RemoteProviderReloadRequest struct {
	Ctxt context.Context `json:"-"`
}

func (e *RemoteProvider) Reload(ro *RemoteProviderReloadRequest) (*RemoteProvider, *ApiErrorResponse, error) {
	gro := &greq.RequestOptions{JSON: ro}
	rs, apierr, err := GetConn(ro.Ctxt).Get(ro.Ctxt, e.Path, gro)
	if apierr != nil {
		return nil, apierr, err
	}
	if err != nil {
		return nil, nil, err
	}
	resp := &RemoteProvider{}
	if err = FillStruct(rs.Data, resp); err != nil {
		return nil, nil, err
	}
	RegisterRemoteProviderEndpoints(resp)
	return resp, nil, nil
}

type RemoteProviderOperationsUpdateRequest struct {
	Ctxt        context.Context `json:"-"`
	OperationId string          `json:"-"`
	Action      string          `json:"action"` //available options are 'clear' and 'abort'
}

func (e *RemoteProvider) CreateOperation(ao *RemoteProviderOperationsUpdateRequest) (*RemoteProvider, *ApiErrorResponse, error) {

	gro := &greq.RequestOptions{JSON: ao}
	rs, apierr, err := GetConn(ao.Ctxt).Put(ao.Ctxt, _path.Join(e.Path, "operations", ao.OperationId), gro)
	if apierr != nil {
		return nil, apierr, err
	}
	if err != nil {
		return nil, nil, err
	}
	resp := &RemoteProvider{}
	if err = FillStruct(rs.Data, resp); err != nil {
		return nil, nil, err
	}
	RegisterRemoteProviderEndpoints(resp)
	return resp, nil, nil
}
<commit_msg>Change name to UpdateOperation<commit_after>package dsdk

import (
	"context"
	_path "path"

	greq "github.com/levigross/grequests"
)

const (
	ProviderAWS    = "AWS S3"
	ProviderGoogle = "Google Cloud"
	ProviderS3     = "S3 Object Store"
)

type RemoteProvider struct {
	Path              string                   `json:"path,omitempty" mapstructure:"path"`
	Uuid              string                   `json:"uuid,omitempty" mapstructure:"uuid"`
	AccountId         string                   `json:"account_id,omitempty" mapstructure:"account_id"`
	RemoteType        string                   `json:"remote_type,omitempty" mapstructure:"remote_type"`
	LastSeenTimestamp string                   `json:"last_seen_timestamp,omitempty" mapstructure:"last_seen_timestamp"`
	Operations        []map[string]interface{} `json:"operations,omitempty" mapstructure:"operations"`
	Snapshots         []*Snapshot              `json:"snapshots,omitempty" mapstructure:"snapshots"`
	Label             string                   `json:"label,omitempty" mapstructure:"label"`
	Status            string                   `json:"status,omitempty" mapstructure:"status"`
	Host              string                   `json:"host,omitempty" mapstructure:"host"`
	Port              string                   `json:"port,omitempty" mapstructure:"port"`
	OperationsEp      string
	SnapshotsEp       *Snapshots
}

func RegisterRemoteProviderEndpoints(rp *RemoteProvider) {
	//a.OperationsEp = newOperations(a.Path)
	rp.SnapshotsEp = newSnapshots(rp.Path)
}

type RemoteProviders struct {
	Path string
}

type RemoteProvidersCreateRequest struct {
	Ctxt        context.Context `json:"-"`
	ProjectName string          `json:"project_name,omitempty" mapstructure:"project_name"`
	AccountId   string          `json:"account_id,omitempty" mapstructure:"account_id"`
	RemoteType  string          `json:"remote_type,omitempty" mapstructure:"remote_type"`
	PrivateKey  string          `json:"private_key,omitempty" mapstructure:"private_key"`
	Label       string          `json:"label,omitempty" mapstructure:"label"`
	Host        string          `json:"host,omitempty" mapstructure:"host"`
	Port        int             `json:"port,omitempty" mapstructure:"port"`
	AccessKey   string          `json:"access_key,omitempty" mapstructure:"access_key"`
	SecretKey   string          `json:"secret_key,omitempty" mapstructure:"secret_key"`
}

func newRemoteProviders(path string) *RemoteProviders {
	return &RemoteProviders{
		Path: _path.Join(path, "remote_providers"),
	}
}

func (e *RemoteProviders) Create(ro *RemoteProvidersCreateRequest) (*RemoteProvider, *ApiErrorResponse, error) {
	gro := &greq.RequestOptions{JSON: ro}
	rs, apierr, err := GetConn(ro.Ctxt).Post(ro.Ctxt, e.Path, gro)
	if apierr != nil {
		return nil, apierr, err
	}
	if err != nil {
		return nil, nil, err
	}
	resp := &RemoteProvider{}
	if err = FillStruct(rs.Data, resp); err != nil {
		return nil, nil, err
	}
	RegisterRemoteProviderEndpoints(resp)
	return resp, nil, nil
}

type RemoteProvidersListRequest struct {
	Ctxt   context.Context `json:"-"`
	Params ListParams      `json:"params,omitempty"`
}

func (e *RemoteProviders) List(ro *RemoteProvidersListRequest) ([]*RemoteProvider, *ApiErrorResponse, error) {
	gro := &greq.RequestOptions{
		JSON:   ro,
		Params: ro.Params.ToMap()}
	rs, apierr, err := GetConn(ro.Ctxt).GetList(ro.Ctxt, e.Path, gro)
	if apierr != nil {
		return nil, apierr, err
	}
	if err != nil {
		return nil, nil, err
	}
	resp := []*RemoteProvider{}
	for _, data := range rs.Data {
		elem := &RemoteProvider{}
		adata := data.(map[string]interface{})
		if err = FillStruct(adata, elem); err != nil {
			return nil, nil, err
		}
		RegisterRemoteProviderEndpoints(elem)
		resp = append(resp, elem)
	}
	return resp, nil, nil
}

type RemoteProvidersGetRequest struct {
	Ctxt context.Context `json:"-"`
	Id   string          `json:"-"`
}

func (e *RemoteProviders) Get(ro *RemoteProvidersGetRequest) (*RemoteProvider, *ApiErrorResponse, error) {
	gro := &greq.RequestOptions{JSON: ro}
	rs, apierr, err := GetConn(ro.Ctxt).Get(ro.Ctxt, _path.Join(e.Path, ro.Id), gro)
	if apierr != nil {
		return nil, apierr, err
	}
	if err != nil {
		return nil, nil, err
	}
	resp := &RemoteProvider{}
	if err = FillStruct(rs.Data, resp); err != nil {
		return nil, nil, err
	}
	RegisterRemoteProviderEndpoints(resp)
	return resp, nil, nil
}

type RemoteProvidersRefreshRequest struct {
	Ctxt context.Context `json:"-"`
	Uuid string          `json:"-"`
}

type RemoteProvidersRefreshResponse struct {
	Uuid string `json:"uuid,omitempty" mapstructure:"uuid"`
}

func (e *RemoteProviders) Refresh(ro *RemoteProvidersRefreshRequest) (*RemoteProvidersRefreshResponse, *ApiErrorResponse, error) {
	gro := &greq.RequestOptions{JSON: ro}
	rs, apierr, err := GetConn(ro.Ctxt).Put(ro.Ctxt, _path.Join(e.Path, ro.Uuid, "refresh"), gro)

	if apierr != nil {
		return nil, apierr, err
	}
	if err != nil {
		return nil, nil, err
	}

	resp := &RemoteProvidersRefreshResponse{}
	if err = FillStruct(rs.Data, resp); err != nil {
		return nil, nil, err
	}

	return resp, nil, nil
}

type RemoteProviderSetRequest struct {
	Ctxt        context.Context `json:"-"`
	ProjectName string          `json:"project_name,omitempty" mapstructure:"project_name"`
	AccountId   string          `json:"account_id,omitempty" mapstructure:"account_id"`
	PrivateKey  string          `json:"private_key,omitempty" mapstructure:"private_key"`
	Label       string          `json:"label,omitempty" mapstructure:"label"`
	Host        string          `json:"host,omitempty" mapstructure:"host"`
	Port        int             `json:"port,omitempty" mapstructure:"port"`
	AccessKey   string          `json:"access_key,omitempty" mapstructure:"access_key"`
	SecretKey   string          `json:"secret_key,omitempty" mapstructure:"secret_key"`
}

func (e *RemoteProvider) Set(ro *RemoteProviderSetRequest) (*RemoteProvider, *ApiErrorResponse, error) {
	gro := &greq.RequestOptions{JSON: ro}
	rs, apierr, err := GetConn(ro.Ctxt).Put(ro.Ctxt, e.Path, gro)
	if apierr != nil {
		return nil, apierr, err
	}
	if err != nil {
		return nil, nil, err
	}
	resp := &RemoteProvider{}
	if err = FillStruct(rs.Data, resp); err != nil {
		return nil, nil, err
	}
	RegisterRemoteProviderEndpoints(resp)
	return resp, nil, nil
}

type RemoteProviderDeleteRequest struct {
	Ctxt  context.Context `json:"-"`
	Force bool            `json:"force,omitempty" mapstructure:"force"`
}

func (e *RemoteProvider) Delete(ro *RemoteProviderDeleteRequest) (*RemoteProvider, *ApiErrorResponse, error) {
	rs, apierr, err := GetConn(ro.Ctxt).Delete(ro.Ctxt, e.Path, nil)
	if apierr != nil {
		return nil, apierr, err
	}
	if err != nil {
		return nil, nil, err
	}
	resp := &RemoteProvider{}
	if err = FillStruct(rs.Data, resp); err != nil {
		return nil, nil, err
	}
	RegisterRemoteProviderEndpoints(resp)
	return resp, nil, nil
}

type RemoteProviderAppTemplate struct {
	Path           string `json:"path,omitempty" mapstructure:"path"`
	ResolvedPath   string `json:"resolved_path,omitempty" mapstructure:"resolved_path"`
	ResolvedTenant string `json:"resolved_tenant,omitempty" mapstructure:"resolved_tenant"`
}

type RemoteProviderReloadRequest struct {
	Ctxt context.Context `json:"-"`
}

func (e *RemoteProvider) Reload(ro *RemoteProviderReloadRequest) (*RemoteProvider, *ApiErrorResponse, error) {
	gro := &greq.RequestOptions{JSON: ro}
	rs, apierr, err := GetConn(ro.Ctxt).Get(ro.Ctxt, e.Path, gro)
	if apierr != nil {
		return nil, apierr, err
	}
	if err != nil {
		return nil, nil, err
	}
	resp := &RemoteProvider{}
	if err = FillStruct(rs.Data, resp); err != nil {
		return nil, nil, err
	}
	RegisterRemoteProviderEndpoints(resp)
	return resp, nil, nil
}

type RemoteProviderOperationsUpdateRequest struct {
	Ctxt        context.Context `json:"-"`
	OperationId string          `json:"-"`
	Action      string          `json:"action"` //available options are 'clear' and 'abort'
}

func (e *RemoteProvider) UpdateOperation(ao *RemoteProviderOperationsUpdateRequest) (*RemoteProvider, *ApiErrorResponse, error) {

	gro := &greq.RequestOptions{JSON: ao}
	rs, apierr, err := GetConn(ao.Ctxt).Put(ao.Ctxt, _path.Join(e.Path, "operations", ao.OperationId), gro)
	if apierr != nil {
		return nil, apierr, err
	}
	if err != nil {
		return nil, nil, err
	}
	resp := &RemoteProvider{}
	if err = FillStruct(rs.Data, resp); err != nil {
		return nil, nil, err
	}
	RegisterRemoteProviderEndpoints(resp)
	return resp, nil, nil
}
<|endoftext|>


<commit_before>// +build !windows

package homedir // import "github.com/docker/docker/pkg/homedir"

import (
	"os"
	"os/user"
)

// Key returns the env var name for the user's home dir based on
// the platform being run on
func Key() string {
	return "HOME"
}

// Get returns the home directory of the current user with the help of
// environment variables depending on the target operating system.
// Returned path should be used with "path/filepath" to form new paths.
// If compiling statically, ensure the osusergo build tag is used.
// If needing to do nss lookups, do not compile statically.
func Get() string {
	home := os.Getenv(Key())
	if home == "" {
		if u, err := user.Current(); err == nil {
			return u.HomeDir
		}
	}
	return home
}

// GetShortcutString returns the string that is shortcut to user's home directory
// in the native shell of the platform running on.
func GetShortcutString() string {
	return "~"
}
<commit_msg>homedir: add cgo or osusergo buildtag constraints for unix<commit_after>// +build !windows,cgo !windows,osusergo

package homedir // import "github.com/docker/docker/pkg/homedir"

import (
	"os"
	"os/user"
)

// Key returns the env var name for the user's home dir based on
// the platform being run on
func Key() string {
	return "HOME"
}

// Get returns the home directory of the current user with the help of
// environment variables depending on the target operating system.
// Returned path should be used with "path/filepath" to form new paths.
// If compiling statically, ensure the osusergo build tag is used.
// If needing to do nss lookups, do not compile statically.
func Get() string {
	home := os.Getenv(Key())
	if home == "" {
		if u, err := user.Current(); err == nil {
			return u.HomeDir
		}
	}
	return home
}

// GetShortcutString returns the string that is shortcut to user's home directory
// in the native shell of the platform running on.
func GetShortcutString() string {
	return "~"
}
<|endoftext|>


<commit_before>/*
Copyright 2020 Google LLC

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 signer

import (
	"github.com/golang/glog"
	"github.com/grafeas/kritis/pkg/kritis/apis/kritis/v1beta1"
	"github.com/grafeas/kritis/pkg/kritis/attestation"
	"github.com/grafeas/kritis/pkg/kritis/crd/authority"
	"github.com/grafeas/kritis/pkg/kritis/cryptolib"
	"github.com/grafeas/kritis/pkg/kritis/metadata"
	"github.com/grafeas/kritis/pkg/kritis/util"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// A signer is used for creating attestations for an image.
type Signer struct {
	config *Config
	client metadata.ReadWriteClient
}

// A signer config that includes necessary data and handler for signing.
type Config struct {
	cSigner cryptolib.Signer
	// an AttestaionAuthority that is used in metadata client APIs.
	// We should consider refactor it out because:
	// 1. the only useful field here is noteName
	// 2. other fields, e.g., public key, are unset
	// TODO: refactor out the authority code
	authority v1beta1.AttestationAuthority
	project   string
}

// Creating a new signer object.
func New(client metadata.ReadWriteClient, cSigner cryptolib.Signer, noteName string, project string) Signer {
	return Signer{
		client: client,
		config: &Config{
			cSigner,
			v1beta1.AttestationAuthority{
				ObjectMeta: metav1.ObjectMeta{Name: "signing-aa"},
				Spec: v1beta1.AttestationAuthoritySpec{
					NoteReference: noteName,
					PublicKeys:    []v1beta1.PublicKey{},
				},
			},
			project,
		},
	}
}

// ImageVulnerabilities is an input for running vulnerability policy validation.
type ImageVulnerabilities struct {
	ImageRef        string
	Vulnerabilities []metadata.Vulnerability
}

// For testing
var (
	authFetcher = authority.Authority
)

// SignImage signs an image without doing any policy check.
// Returns an error if creating an attestation fails.
func (s Signer) SignImage(image string) error {
	existed, _ := s.isAttestationAlreadyExist(image)
	if existed {
		glog.Warningf("Attestation for image %q has already been created.", image)
		return nil
	}

	glog.Infof("Creating attestations for image %q.", image)
	// Create attestation
	att, err := s.createAttestation(image)
	if err != nil {
		return err
	}

	glog.Infof("Uploading attestations for image %q.", image)
	if err := s.uploadAttestation(image, att); err != nil {
		return err
	}
	return nil
}

// Creating an atestation.
func (s Signer) createAttestation(image string) (*cryptolib.Attestation, error) {
	payload, err := attestation.AtomicContainerPayload(image)
	if err != nil {
		return nil, err
	}

	att, err := s.config.cSigner.CreateAttestation(payload)
	if err != nil {
		return nil, err
	}
	return att, nil
}

// Uploading an attestation if not already exist under the same note.
// The method will create a note if it does not already exist.
// Returns error if upload failed, e.g., if an attestation already exists.
func (s Signer) uploadAttestation(image string, att *cryptolib.Attestation) error {
	note, err := util.GetOrCreateAttestationNote(s.client, &s.config.authority)
	if err != nil {
		return err
	}

	// Upload attestation
	_, err = s.client.UploadAttestationOccurrence(note.GetName(), image, att, s.config.project, metadata.PgpSignatureType)
	return err
}

func (s Signer) isAttestationAlreadyExist(image string) (bool, error) {
	atts, err := s.client.Attestations(image, &s.config.authority)
	if err == nil && len(atts) > 0 {
		return true, nil
	}

	return false, err
}
<commit_msg>make signer config internal.<commit_after>/*
Copyright 2020 Google LLC

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 signer

import (
	"github.com/golang/glog"
	"github.com/grafeas/kritis/pkg/kritis/apis/kritis/v1beta1"
	"github.com/grafeas/kritis/pkg/kritis/attestation"
	"github.com/grafeas/kritis/pkg/kritis/crd/authority"
	"github.com/grafeas/kritis/pkg/kritis/cryptolib"
	"github.com/grafeas/kritis/pkg/kritis/metadata"
	"github.com/grafeas/kritis/pkg/kritis/util"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// A signer is used for creating attestations for an image.
type Signer struct {
	config *config
	client metadata.ReadWriteClient
}

// A signer config that includes necessary data and handler for signing.
type config struct {
	cSigner cryptolib.Signer
	// an AttestaionAuthority that is used in metadata client APIs.
	// We should consider refactor it out because:
	// 1. the only useful field here is noteName
	// 2. other fields, e.g., public key, are unset
	// TODO: refactor out the authority code
	authority v1beta1.AttestationAuthority
	project   string
}

// Creating a new signer object.
func New(client metadata.ReadWriteClient, cSigner cryptolib.Signer, noteName string, project string) Signer {
	return Signer{
		client: client,
		config: &config{
			cSigner,
			v1beta1.AttestationAuthority{
				ObjectMeta: metav1.ObjectMeta{Name: "signing-aa"},
				Spec: v1beta1.AttestationAuthoritySpec{
					NoteReference: noteName,
					PublicKeys:    []v1beta1.PublicKey{},
				},
			},
			project,
		},
	}
}

// ImageVulnerabilities is an input for running vulnerability policy validation.
type ImageVulnerabilities struct {
	ImageRef        string
	Vulnerabilities []metadata.Vulnerability
}

// For testing
var (
	authFetcher = authority.Authority
)

// SignImage signs an image without doing any policy check.
// Returns an error if creating an attestation fails.
func (s Signer) SignImage(image string) error {
	existed, _ := s.isAttestationAlreadyExist(image)
	if existed {
		glog.Warningf("Attestation for image %q has already been created.", image)
		return nil
	}

	glog.Infof("Creating attestations for image %q.", image)
	// Create attestation
	att, err := s.createAttestation(image)
	if err != nil {
		return err
	}

	glog.Infof("Uploading attestations for image %q.", image)
	if err := s.uploadAttestation(image, att); err != nil {
		return err
	}
	return nil
}

// Creating an atestation.
func (s Signer) createAttestation(image string) (*cryptolib.Attestation, error) {
	payload, err := attestation.AtomicContainerPayload(image)
	if err != nil {
		return nil, err
	}

	att, err := s.config.cSigner.CreateAttestation(payload)
	if err != nil {
		return nil, err
	}
	return att, nil
}

// Uploading an attestation if not already exist under the same note.
// The method will create a note if it does not already exist.
// Returns error if upload failed, e.g., if an attestation already exists.
func (s Signer) uploadAttestation(image string, att *cryptolib.Attestation) error {
	note, err := util.GetOrCreateAttestationNote(s.client, &s.config.authority)
	if err != nil {
		return err
	}

	// Upload attestation
	_, err = s.client.UploadAttestationOccurrence(note.GetName(), image, att, s.config.project, metadata.PgpSignatureType)
	return err
}

func (s Signer) isAttestationAlreadyExist(image string) (bool, error) {
	atts, err := s.client.Attestations(image, &s.config.authority)
	if err == nil && len(atts) > 0 {
		return true, nil
	}

	return false, err
}
<|endoftext|>


<commit_before>package mapconv

import (
	"testing"
	"time"

	"github.com/stretchr/testify/require"
)

type dummyTagged struct {
	A          string `mapconv:"ValueA.A"`
	B          string `mapconv:"ValueA.ValueB.B"`
	C          string `mapconv:"ValueA.ValueB.ValueC.C"`
	Pointer    *time.Time
	Slice      []string
	NoTag      string
	unexported string
}

type dummyNaked struct {
	ValueA *struct {
		A      string
		ValueB *struct {
			B      string
			ValueC *struct {
				C string
			}
		}
	}
	Pointer    *time.Time
	Slice      []string
	NoTag      string
	unexported string
}

func TestToNaked(t *testing.T) {
	zeroTime := time.Unix(0, 0)
	tests := []struct {
		input  *dummyTagged
		output *dummyNaked
		err    error
	}{
		{
			input: &dummyTagged{
				A:          "A",
				B:          "B",
				C:          "C",
				Pointer:    &zeroTime,
				Slice:      []string{"a", "b", "c"},
				NoTag:      "NoTag",
				unexported: "unexported",
			},
			output: &dummyNaked{
				ValueA: &struct {
					A      string
					ValueB *struct {
						B      string
						ValueC *struct {
							C string
						}
					}
				}{
					A: "A",
					ValueB: &struct {
						B      string
						ValueC *struct {
							C string
						}
					}{
						B: "B",
						ValueC: &struct {
							C string
						}{
							C: "C",
						},
					},
				},
				Pointer: &zeroTime,
				Slice:   []string{"a", "b", "c"},
				NoTag:   "NoTag",
			},
		},
	}

	for _, tt := range tests {
		output := &dummyNaked{}
		err := ConvertTo(tt.input, output)
		require.Equal(t, tt.err, err)
		if err == nil {
			require.EqualValues(t, tt.output.ValueA, output.ValueA)
			require.EqualValues(t, tt.output.Pointer.String(), output.Pointer.String())
			require.EqualValues(t, tt.output.Slice, output.Slice)
			require.EqualValues(t, tt.output.NoTag, output.NoTag)
		}
	}

}

func TestFromNaked(t *testing.T) {

	tests := []struct {
		output *dummyTagged
		input  *dummyNaked
		err    error
	}{
		{
			output: &dummyTagged{
				A:     "A",
				B:     "B",
				C:     "C",
				NoTag: "NoTag",
			},
			input: &dummyNaked{
				ValueA: &struct {
					A      string
					ValueB *struct {
						B      string
						ValueC *struct {
							C string
						}
					}
				}{
					A: "A",
					ValueB: &struct {
						B      string
						ValueC *struct {
							C string
						}
					}{
						B: "B",
						ValueC: &struct {
							C string
						}{
							C: "C",
						},
					},
				},
				NoTag: "NoTag",
			},
		},
	}

	for _, tt := range tests {
		output := &dummyTagged{}
		err := ConvertFrom(tt.input, output)
		require.Equal(t, tt.err, err)
		if err == nil {
			require.Equal(t, tt.output, output)
		}
	}

}

type dummySlice struct {
	Slice []*dummySliceInner `json:",omitempty"`
}

type dummySliceInner struct {
	Value string             `json:",omitempty"`
	Slice []*dummySliceInner `json:",omitempty"`
}

type dummyExtractInnerSlice struct {
	Values       []string `json:",omitempty" mapconv:"[]Slice.Value"`
	NestedValues []string `json:",omitempty" mapconv:"[]Slice.[]Slice.Value"`
}

func TestExtractInnerSlice(t *testing.T) {
	tests := []struct {
		input  *dummySlice
		expect *dummyExtractInnerSlice
	}{
		{
			input: &dummySlice{
				Slice: []*dummySliceInner{
					{Value: "value1"},
					{Value: "value2"},
					{
						Value: "value3",
						Slice: []*dummySliceInner{
							{Value: "value4"},
							{Value: "value5"},
						},
					},
				},
			},
			expect: &dummyExtractInnerSlice{
				Values:       []string{"value1", "value2", "value3"},
				NestedValues: []string{"value4", "value5"},
			},
		},
	}

	for _, tt := range tests {
		output := &dummyExtractInnerSlice{}
		err := ConvertFrom(tt.input, output)

		require.NoError(t, err)
		require.Equal(t, tt.expect, output)
	}
}

func TestInsertInnerSlice(t *testing.T) {
	tests := []struct {
		input  *dummyExtractInnerSlice
		output *dummySlice
	}{
		{
			input: &dummyExtractInnerSlice{
				Values:       []string{"value1", "value2", "value3"},
				NestedValues: []string{"value4", "value5"},
			},
			output: &dummySlice{
				Slice: []*dummySliceInner{
					{Value: "value1"},
					{Value: "value2"},
					{Value: "value3"},
					{
						Slice: []*dummySliceInner{
							{Value: "value4"},
						},
					},
					{
						Slice: []*dummySliceInner{
							{Value: "value5"},
						},
					},
				},
			},
		},
	}

	for _, tt := range tests {
		output := &dummySlice{}
		err := ConvertTo(tt.input, output)

		require.NoError(t, err)
		require.Equal(t, tt.output, output)
	}
}

type hasDefaultSource struct {
	Field string `mapconv:"Field,default=default-value"`
}

type hasDefaultDest struct {
	Field string
}

func TestDefaultValue(t *testing.T) {
	tests := []struct {
		input  *hasDefaultSource
		output *hasDefaultDest
	}{
		{
			input: &hasDefaultSource{},
			output: &hasDefaultDest{
				Field: "default-value",
			},
		},
	}

	for _, tt := range tests {
		output := &hasDefaultDest{}
		err := ConvertTo(tt.input, output)
		require.NoError(t, err)
		require.Equal(t, tt.output, output)
	}
}

type multipleSource struct {
	Field string `mapconv:"Field1/Field2"`
}

type multipleDest struct {
	Field1 string
	Field2 string
}

func TestMultipleDestination(t *testing.T) {
	tests := []struct {
		input  *multipleSource
		output *multipleDest
	}{
		{
			input: &multipleSource{
				Field: "value",
			},
			output: &multipleDest{
				Field1: "value",
				Field2: "value",
			},
		},
	}

	for _, tt := range tests {
		output := &multipleDest{}
		err := ConvertTo(tt.input, output)
		require.NoError(t, err)
		require.Equal(t, tt.output, output)
	}
}

type recursiveSource struct {
	Field *recursiveSourceChild `mapconv:",recursive"`
}

type recursiveSourceChild struct {
	Field1 string `mapconv:"Dest1"`
	Field2 string `mapconv:"Dest2"`
}

type recursiveDest struct {
	Field *recursiveDestChild
}

type recursiveDestChild struct {
	Dest1 string
	Dest2 string
}

type recursiveSourceSlice struct {
	Fields []*recursiveSourceChild `mapconv:"[]Slice,recursive"`
}

type recursiveDestSlice struct {
	Slice []*recursiveDestChild
}

func TestRecursive(t *testing.T) {
	tests := []struct {
		input  *recursiveSource
		expect *recursiveDest
	}{
		{
			input: &recursiveSource{
				Field: &recursiveSourceChild{
					Field1: "value1",
					Field2: "value2",
				},
			},
			expect: &recursiveDest{
				Field: &recursiveDestChild{
					Dest1: "value1",
					Dest2: "value2",
				},
			},
		},
	}

	for _, tt := range tests {
		dest := &recursiveDest{}
		err := ConvertTo(tt.input, dest)
		require.NoError(t, err)
		require.Equal(t, tt.expect, dest)

		// reverse
		source := &recursiveSource{}
		err = ConvertFrom(tt.expect, source)
		require.NoError(t, err)
		require.Equal(t, tt.input, source)
	}
}

func TestRecursiveSlice(t *testing.T) {
	tests := []struct {
		input  *recursiveSourceSlice
		output *recursiveDestSlice
	}{
		{
			input: &recursiveSourceSlice{
				Fields: []*recursiveSourceChild{
					{
						Field1: "value1",
						Field2: "value2",
					},
					{
						Field1: "value3",
						Field2: "value4",
					},
				},
			},
			output: &recursiveDestSlice{
				Slice: []*recursiveDestChild{
					{
						Dest1: "value1",
						Dest2: "value2",
					},
					{
						Dest1: "value3",
						Dest2: "value4",
					},
				},
			},
		},
	}

	for _, tt := range tests {
		output := &recursiveDestSlice{}
		err := ConvertTo(tt.input, output)
		require.NoError(t, err)
		require.Equal(t, tt.output, output)

		// reverse
		source := &recursiveSourceSlice{}
		err = ConvertFrom(tt.output, source)
		require.NoError(t, err)
		require.Equal(t, tt.input, source)
	}
}

type sourceSquash struct {
	Field *sourceSquashChild `mapconv:",squash"`
}

type sourceSquashChild struct {
	Field1 string
	Field2 string
}

type destSquash struct {
	Field1 string
	Field2 string
}

func TestSquash(t *testing.T) {
	tests := []struct {
		input  *sourceSquash
		output *destSquash
	}{
		{
			input: &sourceSquash{
				Field: &sourceSquashChild{
					Field1: "f1",
					Field2: "f2",
				},
			},
			output: &destSquash{
				Field1: "f1",
				Field2: "f2",
			},
		},
	}

	for _, tt := range tests {
		output := &destSquash{}
		err := ConvertTo(tt.input, output)
		require.NoError(t, err)
		require.Equal(t, tt.output, output)

		// reverse
		source := &sourceSquash{}
		err = ConvertFrom(tt.output, source)
		require.Error(t, err)
	}
}
<commit_msg>mapconv: Improve test cases<commit_after>package mapconv

import (
	"testing"
	"time"

	"github.com/stretchr/testify/require"
)

type dummyFrom struct {
	A          string `mapconv:"ValueA.A"`
	B          string `mapconv:"ValueA.ValueB.B"`
	C          string `mapconv:"ValueA.ValueB.ValueC.C"`
	Pointer    *time.Time
	Slice      []string
	NoTag      string
	Bool       bool
	unexported string
}

type dummyTo struct {
	ValueA *struct {
		A      string
		ValueB *struct {
			B      string
			ValueC *struct {
				C string
			}
		}
	}
	Pointer    *time.Time
	Slice      []string
	NoTag      string
	Bool       bool
	unexported string
}

func TestConvertTo(t *testing.T) {
	zeroTime := time.Unix(0, 0)
	tests := []struct {
		input  *dummyFrom
		output *dummyTo
		err    error
	}{
		{
			input: &dummyFrom{
				A:          "A",
				B:          "B",
				C:          "C",
				Pointer:    &zeroTime,
				Slice:      []string{"a", "b", "c"},
				NoTag:      "NoTag",
				Bool:       true,
				unexported: "unexported",
			},
			output: &dummyTo{
				ValueA: &struct {
					A      string
					ValueB *struct {
						B      string
						ValueC *struct {
							C string
						}
					}
				}{
					A: "A",
					ValueB: &struct {
						B      string
						ValueC *struct {
							C string
						}
					}{
						B: "B",
						ValueC: &struct {
							C string
						}{
							C: "C",
						},
					},
				},
				Pointer: &zeroTime,
				Slice:   []string{"a", "b", "c"},
				NoTag:   "NoTag",
				Bool:    true,
			},
		},
	}

	for _, tt := range tests {
		output := &dummyTo{}
		err := ConvertTo(tt.input, output)
		require.Equal(t, tt.err, err)
		if err == nil {
			require.EqualValues(t, tt.output.ValueA, output.ValueA)
			require.EqualValues(t, tt.output.Pointer.String(), output.Pointer.String())
			require.EqualValues(t, tt.output.Slice, output.Slice)
			require.EqualValues(t, tt.output.NoTag, output.NoTag)
		}
	}

}

func TestConvertFrom(t *testing.T) {

	tests := []struct {
		output *dummyFrom
		input  *dummyTo
		err    error
	}{
		{
			output: &dummyFrom{
				A:     "A",
				B:     "B",
				C:     "C",
				NoTag: "NoTag",
				Bool:  true,
			},
			input: &dummyTo{
				ValueA: &struct {
					A      string
					ValueB *struct {
						B      string
						ValueC *struct {
							C string
						}
					}
				}{
					A: "A",
					ValueB: &struct {
						B      string
						ValueC *struct {
							C string
						}
					}{
						B: "B",
						ValueC: &struct {
							C string
						}{
							C: "C",
						},
					},
				},
				NoTag: "NoTag",
				Bool:  true,
			},
		},
	}

	for _, tt := range tests {
		output := &dummyFrom{}
		err := ConvertFrom(tt.input, output)
		require.Equal(t, tt.err, err)
		if err == nil {
			require.Equal(t, tt.output, output)
		}
	}

}

type dummySlice struct {
	Slice []*dummySliceInner `json:",omitempty"`
}

type dummySliceInner struct {
	Value string             `json:",omitempty"`
	Slice []*dummySliceInner `json:",omitempty"`
}

type dummyExtractInnerSlice struct {
	Values       []string `json:",omitempty" mapconv:"[]Slice.Value"`
	NestedValues []string `json:",omitempty" mapconv:"[]Slice.[]Slice.Value"`
}

func TestExtractInnerSlice(t *testing.T) {
	tests := []struct {
		input  *dummySlice
		expect *dummyExtractInnerSlice
	}{
		{
			input: &dummySlice{
				Slice: []*dummySliceInner{
					{Value: "value1"},
					{Value: "value2"},
					{
						Value: "value3",
						Slice: []*dummySliceInner{
							{Value: "value4"},
							{Value: "value5"},
						},
					},
				},
			},
			expect: &dummyExtractInnerSlice{
				Values:       []string{"value1", "value2", "value3"},
				NestedValues: []string{"value4", "value5"},
			},
		},
	}

	for _, tt := range tests {
		output := &dummyExtractInnerSlice{}
		err := ConvertFrom(tt.input, output)

		require.NoError(t, err)
		require.Equal(t, tt.expect, output)
	}
}

func TestInsertInnerSlice(t *testing.T) {
	tests := []struct {
		input  *dummyExtractInnerSlice
		output *dummySlice
	}{
		{
			input: &dummyExtractInnerSlice{
				Values:       []string{"value1", "value2", "value3"},
				NestedValues: []string{"value4", "value5"},
			},
			output: &dummySlice{
				Slice: []*dummySliceInner{
					{Value: "value1"},
					{Value: "value2"},
					{Value: "value3"},
					{
						Slice: []*dummySliceInner{
							{Value: "value4"},
						},
					},
					{
						Slice: []*dummySliceInner{
							{Value: "value5"},
						},
					},
				},
			},
		},
	}

	for _, tt := range tests {
		output := &dummySlice{}
		err := ConvertTo(tt.input, output)

		require.NoError(t, err)
		require.Equal(t, tt.output, output)
	}
}

type hasDefaultSource struct {
	Field string `mapconv:"Field,default=default-value"`
}

type hasDefaultDest struct {
	Field string
}

func TestDefaultValue(t *testing.T) {
	tests := []struct {
		input  *hasDefaultSource
		output *hasDefaultDest
	}{
		{
			input: &hasDefaultSource{},
			output: &hasDefaultDest{
				Field: "default-value",
			},
		},
	}

	for _, tt := range tests {
		output := &hasDefaultDest{}
		err := ConvertTo(tt.input, output)
		require.NoError(t, err)
		require.Equal(t, tt.output, output)
	}
}

type multipleSource struct {
	Field string `mapconv:"Field1/Field2"`
}

type multipleDest struct {
	Field1 string
	Field2 string
}

func TestMultipleDestination(t *testing.T) {
	tests := []struct {
		input  *multipleSource
		output *multipleDest
	}{
		{
			input: &multipleSource{
				Field: "value",
			},
			output: &multipleDest{
				Field1: "value",
				Field2: "value",
			},
		},
	}

	for _, tt := range tests {
		output := &multipleDest{}
		err := ConvertTo(tt.input, output)
		require.NoError(t, err)
		require.Equal(t, tt.output, output)
	}
}

type recursiveSource struct {
	Field *recursiveSourceChild `mapconv:",recursive"`
}

type recursiveSourceChild struct {
	Field1 string `mapconv:"Dest1"`
	Field2 string `mapconv:"Dest2"`
}

type recursiveDest struct {
	Field *recursiveDestChild
}

type recursiveDestChild struct {
	Dest1 string
	Dest2 string
}

type recursiveSourceSlice struct {
	Fields []*recursiveSourceChild `mapconv:"[]Slice,recursive"`
}

type recursiveDestSlice struct {
	Slice []*recursiveDestChild
}

func TestRecursive(t *testing.T) {
	tests := []struct {
		input  *recursiveSource
		expect *recursiveDest
	}{
		{
			input: &recursiveSource{
				Field: &recursiveSourceChild{
					Field1: "value1",
					Field2: "value2",
				},
			},
			expect: &recursiveDest{
				Field: &recursiveDestChild{
					Dest1: "value1",
					Dest2: "value2",
				},
			},
		},
	}

	for _, tt := range tests {
		dest := &recursiveDest{}
		err := ConvertTo(tt.input, dest)
		require.NoError(t, err)
		require.Equal(t, tt.expect, dest)

		// reverse
		source := &recursiveSource{}
		err = ConvertFrom(tt.expect, source)
		require.NoError(t, err)
		require.Equal(t, tt.input, source)
	}
}

func TestRecursiveSlice(t *testing.T) {
	tests := []struct {
		input  *recursiveSourceSlice
		output *recursiveDestSlice
	}{
		{
			input: &recursiveSourceSlice{
				Fields: []*recursiveSourceChild{
					{
						Field1: "value1",
						Field2: "value2",
					},
					{
						Field1: "value3",
						Field2: "value4",
					},
				},
			},
			output: &recursiveDestSlice{
				Slice: []*recursiveDestChild{
					{
						Dest1: "value1",
						Dest2: "value2",
					},
					{
						Dest1: "value3",
						Dest2: "value4",
					},
				},
			},
		},
	}

	for _, tt := range tests {
		output := &recursiveDestSlice{}
		err := ConvertTo(tt.input, output)
		require.NoError(t, err)
		require.Equal(t, tt.output, output)

		// reverse
		source := &recursiveSourceSlice{}
		err = ConvertFrom(tt.output, source)
		require.NoError(t, err)
		require.Equal(t, tt.input, source)
	}
}

type sourceSquash struct {
	Field *sourceSquashChild `mapconv:",squash"`
}

type sourceSquashChild struct {
	Field1 string
	Field2 string
}

type destSquash struct {
	Field1 string
	Field2 string
}

func TestSquash(t *testing.T) {
	tests := []struct {
		input  *sourceSquash
		output *destSquash
	}{
		{
			input: &sourceSquash{
				Field: &sourceSquashChild{
					Field1: "f1",
					Field2: "f2",
				},
			},
			output: &destSquash{
				Field1: "f1",
				Field2: "f2",
			},
		},
	}

	for _, tt := range tests {
		output := &destSquash{}
		err := ConvertTo(tt.input, output)
		require.NoError(t, err)
		require.Equal(t, tt.output, output)

		// reverse
		source := &sourceSquash{}
		err = ConvertFrom(tt.output, source)
		require.Error(t, err)
	}
}
<|endoftext|>


<commit_before>package github

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"regexp"
	"strings"

	"github.com/codegangsta/cli"

	"github.com/scraperwiki/hookbot/pkg/hookbot"
	"github.com/scraperwiki/hookbot/pkg/listen"
)

var RegexParseHeader = regexp.MustCompile("^\\s*([^\\:]+)\\s*:\\s*(.*)$")

func MustParseHeader(header string) (string, string) {
	if !RegexParseHeader.MatchString(header) {
		log.Fatalf("Unable to parse header: %v (re: %v)", header,
			RegexParseHeader.String())
		return "", ""
	}

	parts := RegexParseHeader.FindStringSubmatch(header)
	return parts[1], parts[2]
}

func MustParseHeaders(headerStrings []string) http.Header {
	headers := http.Header{}

	for _, h := range headerStrings {
		key, value := MustParseHeader(h)
		headers.Set(key, value)
	}

	return headers
}

func MustMakeHeader(
	target *url.URL, origin string, headerStrings []string,
) http.Header {

	header := MustParseHeaders(headerStrings)
	if origin == "samehost" {
		origin = "//" + target.Host
	}

	header.Add("Origin", origin)
	header.Add("X-Hookbot-Unsafe-Is-Ok",
		"I understand the security implications")

	return header
}

func ActionRoute(c *cli.Context) {

	target, err := url.Parse(c.String("monitor-url"))
	if err != nil {
		log.Fatalf("Failed to parse %q as URL: %v", c.String("monitor-url"), err)
	}

	origin := c.String("origin")

	header := MustMakeHeader(target, origin, c.StringSlice("header"))
	finish := make(chan struct{})

	messages, errors := listen.RetryingWatch(target.String(), header, finish)

	outbound := make(chan listen.Message, 1)

	send := func(endpoint string, payload []byte) {
		token := Sha1HMAC(c.GlobalString("key"), []byte(endpoint))

		outURL := fmt.Sprintf("https://%v@%v/pub/%v", token, target.Host, endpoint)

		body := ioutil.NopCloser(bytes.NewBuffer(payload))

		out, err := http.NewRequest("POST", outURL, body)
		if err != nil {
			log.Printf("Failed to construct outbound req: %v", err)
			return
		}
		out.SetBasicAuth(token, "")

		resp, err := http.DefaultClient.Do(out)
		if err != nil {
			log.Printf("Failed to transmit: %v", err)
			return
		}
		log.Printf("Transmit: %v %v", resp.StatusCode, outURL)
	}

	go func() {
		for err := range errors {
			log.Printf("Encountered error in Watch: %v", err)
		}
	}()

	for m := range messages {
		log.Printf("Receive message")
		if !IsValidGithubSignature(c.GlobalString("github-secret"), m) {
			log.Printf("Reject github signature")
			continue
		}

		Route(m, send)
	}
	close(outbound)
}

type Event struct {
	Type string

	Repository *Repository `json:"repository"`
	Pusher     *Pusher     `json:"pusher"`

	Ref    string `json:"ref"`
	Before string `json:"before"`
	After  string `json:"after"`
}

func (e *Event) Branch() string {
	return strings.TrimPrefix(e.Ref, "refs/heads/")
}

type Pusher struct {
	Name  string `json:"name"`
	Email string `json:"email"`
}

type Repository struct {
	FullName string `json:"full_name"`
}

func Route(message []byte, send func(string, []byte)) {

	type GithubMessage struct {
		Event, Signature string
		Payload          []byte
	}

	var m GithubMessage

	err := json.Unmarshal(message, &m)
	if err != nil {
		log.Printf("Failed to unmarshal message in IsValidGithubSignature: %v",
			err)
		return
	}

	var event Event
	event.Type = m.Event

	err = json.Unmarshal(m.Payload, &event)
	if err != nil {
		log.Printf("Route: error in json.Unmarshal: %v", err)
		return
	}

	if event.Repository == nil || event.Repository.FullName == "" {
		log.Printf("Could not identify repository for event %v", event.Type)
		return
	}

	repo := event.Repository.FullName
	branch := event.Branch()

	who := "<unknown>"
	if event.Pusher != nil {
		who = event.Pusher.Name
	}

	msgBytes, err := json.Marshal(map[string]string{
		"Type":   event.Type,
		"Repo":   repo,
		"Branch": branch,
		"SHA":    event.After,
		"Who":    who,
	})
	if err != nil {
		log.Printf("Failed to marshal Update: %v", err)
		return
	}

	switch event.Type {
	case "push":
		topicFmt := "github.com/repo/%s/branch/%s"
		topic := fmt.Sprintf(topicFmt, repo, branch)
		send(topic, msgBytes)
	default:
		log.Printf("Unhandled event type: %v", event.Type)
		return
	}
}

type Router struct{}

func (r *Router) Name() string {
	return "github"
}

func (r *Router) Topics() []string {
	return []string{"/unsafe/github.com/?recursive"}
}

func (r *Router) Route(in hookbot.Message, publish func(hookbot.Message) bool) {

	log.Printf("route github: %q", in.Topic)

	type GithubMessage struct {
		Event, Signature string
		Payload          []byte
	}

	var m GithubMessage

	err := json.Unmarshal(in.Body, &m)
	if err != nil {
		log.Printf("Failed to unmarshal message in IsValidGithubSignature: %v",
			err)
		return
	}

	var event Event
	event.Type = m.Event

	err = json.Unmarshal(m.Payload, &event)
	if err != nil {
		log.Printf("Route: error in json.Unmarshal: %v", err)
		return
	}

	if event.Repository == nil || event.Repository.FullName == "" {
		log.Printf("Could not identify repository for event %v", event.Type)
		return
	}

	repo := event.Repository.FullName
	branch := event.Branch()

	who := "<unknown>"
	if event.Pusher != nil {
		who = event.Pusher.Name
	}

	msgBytes, err := json.Marshal(map[string]string{
		"Type":   event.Type,
		"Repo":   repo,
		"Branch": branch,
		"SHA":    event.After,
		"Who":    who,
	})
	if err != nil {
		log.Printf("Failed to marshal Update: %v", err)
		return
	}

	switch event.Type {
	case "push":
		topicFmt := "github.com/repo/%s/branch/%s"

		// May fail
		_ = publish(hookbot.Message{
			Topic: fmt.Sprintf(topicFmt, repo, branch),
			Body:  msgBytes,
		})
	default:
		log.Printf("Unhandled event type: %v", event.Type)
		return
	}

}

func init() {
	hookbot.RegisterRouter(&Router{})
}
<commit_msg>Rewrite standalone router to remove dup'd code<commit_after>package github

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"regexp"
	"strings"

	"github.com/codegangsta/cli"

	"github.com/scraperwiki/hookbot/pkg/hookbot"
	"github.com/scraperwiki/hookbot/pkg/listen"
)

var RegexParseHeader = regexp.MustCompile("^\\s*([^\\:]+)\\s*:\\s*(.*)$")

func MustParseHeader(header string) (string, string) {
	if !RegexParseHeader.MatchString(header) {
		log.Fatalf("Unable to parse header: %v (re: %v)", header,
			RegexParseHeader.String())
		return "", ""
	}

	parts := RegexParseHeader.FindStringSubmatch(header)
	return parts[1], parts[2]
}

func MustParseHeaders(headerStrings []string) http.Header {
	headers := http.Header{}

	for _, h := range headerStrings {
		key, value := MustParseHeader(h)
		headers.Set(key, value)
	}

	return headers
}

func MustMakeHeader(
	target *url.URL, origin string, headerStrings []string,
) http.Header {

	header := MustParseHeaders(headerStrings)
	if origin == "samehost" {
		origin = "//" + target.Host
	}

	header.Add("Origin", origin)
	header.Add("X-Hookbot-Unsafe-Is-Ok",
		"I understand the security implications")

	return header
}

func ActionRoute(c *cli.Context) {

	target, err := url.Parse(c.String("monitor-url"))
	if err != nil {
		log.Fatalf("Failed to parse %q as URL: %v", c.String("monitor-url"), err)
	}

	origin := c.String("origin")

	header := MustMakeHeader(target, origin, c.StringSlice("header"))
	finish := make(chan struct{})

	messages, errors := listen.RetryingWatch(target.String(), header, finish)

	outbound := make(chan listen.Message, 1)

	publish := func(m hookbot.Message) bool {
		token := Sha1HMAC(c.GlobalString("key"), []byte(m.Topic))

		outURL := fmt.Sprintf("https://%v@%v/pub/%s", token, target.Host, m.Topic)

		body := ioutil.NopCloser(bytes.NewBuffer(m.Body))

		out, err := http.NewRequest("POST", outURL, body)
		if err != nil {
			log.Printf("Failed to construct outbound req: %v", err)
			return false
		}
		out.SetBasicAuth(token, "")

		resp, err := http.DefaultClient.Do(out)
		if err != nil {
			log.Printf("Failed to transmit: %v", err)
			return false
		}
		log.Printf("Transmit: %v %v", resp.StatusCode, outURL)
		return true
	}

	go func() {
		for err := range errors {
			log.Printf("Encountered error in Watch: %v", err)
		}
	}()

	router := &Router{}

	for mBytes := range messages {
		log.Printf("Receive message")

		parts := bytes.Split(mBytes, []byte{0})
		topic := parts[0]
		body := parts[1]

		if !IsValidGithubSignature(c.GlobalString("github-secret"), body) {
			log.Printf("Reject github signature")
			continue
		}

		m := hookbot.Message{Topic: string(topic), Body: body}
		router.Route(m, publish)
	}
	close(outbound)
}

type Event struct {
	Type string

	Repository *Repository `json:"repository"`
	Pusher     *Pusher     `json:"pusher"`

	Ref    string `json:"ref"`
	Before string `json:"before"`
	After  string `json:"after"`
}

func (e *Event) Branch() string {
	return strings.TrimPrefix(e.Ref, "refs/heads/")
}

type Pusher struct {
	Name  string `json:"name"`
	Email string `json:"email"`
}

type Repository struct {
	FullName string `json:"full_name"`
}

type Router struct{}

func (r *Router) Name() string {
	return "github"
}

func (r *Router) Topics() []string {
	return []string{"/unsafe/github.com/?recursive"}
}

func (r *Router) Route(in hookbot.Message, publish func(hookbot.Message) bool) {

	log.Printf("route github: %q", in.Topic)

	type GithubMessage struct {
		Event, Signature string
		Payload          []byte
	}

	var m GithubMessage

	err := json.Unmarshal(in.Body, &m)
	if err != nil {
		log.Printf("Failed to unmarshal message in IsValidGithubSignature: %v",
			err)
		return
	}

	var event Event
	event.Type = m.Event

	err = json.Unmarshal(m.Payload, &event)
	if err != nil {
		log.Printf("Route: error in json.Unmarshal: %v", err)
		return
	}

	if event.Repository == nil || event.Repository.FullName == "" {
		log.Printf("Could not identify repository for event %v", event.Type)
		return
	}

	repo := event.Repository.FullName
	branch := event.Branch()

	who := "<unknown>"
	if event.Pusher != nil {
		who = event.Pusher.Name
	}

	msgBytes, err := json.Marshal(map[string]string{
		"Type":   event.Type,
		"Repo":   repo,
		"Branch": branch,
		"SHA":    event.After,
		"Who":    who,
	})
	if err != nil {
		log.Printf("Failed to marshal Update: %v", err)
		return
	}

	switch event.Type {
	case "push":
		topicFmt := "github.com/repo/%s/branch/%s"

		// May fail
		_ = publish(hookbot.Message{
			Topic: fmt.Sprintf(topicFmt, repo, branch),
			Body:  msgBytes,
		})
	default:
		log.Printf("Unhandled event type: %v", event.Type)
		return
	}

}

func init() {
	hookbot.RegisterRouter(&Router{})
}
<|endoftext|>


<commit_before>package manager

import (
	"bytes"
	"context"
	"io/ioutil"
	"strings"
	"time"

	"github.com/cortexproject/cortex/pkg/ruler"
	"github.com/go-kit/kit/log"
	"github.com/pkg/errors"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/model"
	"github.com/prometheus/prometheus/notifier"
	"github.com/prometheus/prometheus/pkg/labels"
	"github.com/prometheus/prometheus/pkg/rulefmt"
	"github.com/prometheus/prometheus/pkg/timestamp"
	"github.com/prometheus/prometheus/promql"
	"github.com/prometheus/prometheus/promql/parser"
	"github.com/prometheus/prometheus/rules"
	"github.com/prometheus/prometheus/template"
	"github.com/weaveworks/common/user"
	yaml "gopkg.in/yaml.v3"

	"github.com/grafana/loki/pkg/logproto"
	"github.com/grafana/loki/pkg/logql"
)

// engineQueryFunc returns a new query function using the rules.EngineQueryFunc function
// and passing an altered timestamp.
func engineQueryFunc(engine *logql.Engine, delay time.Duration) rules.QueryFunc {
	return rules.QueryFunc(func(ctx context.Context, qs string, t time.Time) (promql.Vector, error) {
		adjusted := t.Add(-delay)
		params := logql.NewLiteralParams(
			qs,
			adjusted,
			adjusted,
			0,
			0,
			logproto.FORWARD,
			0,
			nil,
		)
		q := engine.Query(params)

		res, err := q.Exec(ctx)
		if err != nil {
			return nil, err
		}
		switch v := res.Data.(type) {
		case promql.Vector:
			return v, nil
		case promql.Scalar:
			return promql.Vector{promql.Sample{
				Point:  promql.Point(v),
				Metric: labels.Labels{},
			}}, nil
		default:
			return nil, errors.New("rule result is not a vector or scalar")
		}
	})

}

// MultiTenantManagerAdapter will wrap a MultiTenantManager which validates loki rules
func MultiTenantManagerAdapter(mgr ruler.MultiTenantManager) *MultiTenantManager {
	return &MultiTenantManager{mgr}
}

// MultiTenantManager wraps a cortex MultiTenantManager but validates loki rules
type MultiTenantManager struct {
	ruler.MultiTenantManager
}

// ValidateRuleGroup validates a rulegroup
func (m *MultiTenantManager) ValidateRuleGroup(grp rulefmt.RuleGroup) []error {
	return validateGroups(grp)
}

func MemstoreTenantManager(
	cfg ruler.Config,
	engine *logql.Engine,
) ruler.ManagerFactory {
	var metrics *Metrics

	return func(
		ctx context.Context,
		userID string,
		notifier *notifier.Manager,
		logger log.Logger,
		reg prometheus.Registerer,
	) *rules.Manager {

		// We'll ignore the passed registere and use the default registerer to avoid prefix issues and other weirdness.
		// This closure prevents re-registering.
		if metrics == nil {
			metrics = NewMetrics(prometheus.DefaultRegisterer)
		}
		logger = log.With(logger, "user", userID)
		queryFunc := engineQueryFunc(engine, cfg.EvaluationDelay)
		memStore := NewMemStore(userID, queryFunc, metrics, 5*time.Minute, log.With(logger, "subcomponent", "MemStore"))

		mgr := rules.NewManager(&rules.ManagerOptions{
			Appendable:      NoopAppender{},
			Queryable:       memStore,
			QueryFunc:       queryFunc,
			Context:         user.InjectOrgID(ctx, userID),
			ExternalURL:     cfg.ExternalURL.URL,
			NotifyFunc:      ruler.SendAlerts(notifier, cfg.ExternalURL.URL.String()),
			Logger:          logger,
			Registerer:      reg,
			OutageTolerance: cfg.OutageTolerance,
			ForGracePeriod:  cfg.ForGracePeriod,
			ResendDelay:     cfg.ResendDelay,
			GroupLoader:     GroupLoader{},
		})

		// initialize memStore, bound to the manager's alerting rules
		memStore.Start(mgr)

		return mgr
	}
}

type GroupLoader struct{}

func (GroupLoader) Parse(query string) (parser.Expr, error) {
	expr, err := logql.ParseExpr(query)
	if err != nil {
		return nil, err
	}

	return exprAdapter{expr}, nil
}

func (g GroupLoader) Load(identifier string) (*rulefmt.RuleGroups, []error) {
	b, err := ioutil.ReadFile(identifier)
	if err != nil {
		return nil, []error{errors.Wrap(err, identifier)}
	}
	rgs, errs := g.parseRules(b)
	for i := range errs {
		errs[i] = errors.Wrap(errs[i], identifier)
	}
	return rgs, errs
}

func (GroupLoader) parseRules(content []byte) (*rulefmt.RuleGroups, []error) {
	var (
		groups rulefmt.RuleGroups
		errs   []error
	)

	decoder := yaml.NewDecoder(bytes.NewReader(content))
	decoder.KnownFields(true)

	if err := decoder.Decode(&groups); err != nil {
		errs = append(errs, err)
	}

	if len(errs) > 0 {
		return nil, errs
	}

	return &groups, validateGroups(groups.Groups...)
}

func validateGroups(grps ...rulefmt.RuleGroup) (errs []error) {
	set := map[string]struct{}{}

	for i, g := range grps {
		if g.Name == "" {
			errs = append(errs, errors.Errorf("group %d: Groupname must not be empty", i))
		}

		if _, ok := set[g.Name]; ok {
			errs = append(
				errs,
				errors.Errorf("groupname: \"%s\" is repeated in the same file", g.Name),
			)
		}

		set[g.Name] = struct{}{}

		for _, r := range g.Rules {
			if err := validateRuleNode(&r); err != nil {
				errs = append(errs, err)
			}
		}
	}

	return errs
}

func validateRuleNode(r *rulefmt.RuleNode) error {
	if r.Record.Value != "" && r.Alert.Value != "" {
		return errors.Errorf("only one of 'record' and 'alert' must be set")
	}

	if r.Record.Value == "" && r.Alert.Value == "" {
		return errors.Errorf("one of 'record' or 'alert' must be set")
	}

	if r.Record.Value != "" && r.Alert.Value != "" {
		return errors.Errorf("only one of 'record' or 'alert' must be set")
	}

	if r.Expr.Value == "" {
		return errors.Errorf("field 'expr' must be set in rule")
	} else if _, err := logql.ParseExpr(r.Expr.Value); err != nil {
		return errors.Wrapf(err, "could not parse expression")
	}

	if r.Record.Value != "" {
		if len(r.Annotations) > 0 {
			return errors.Errorf("invalid field 'annotations' in recording rule")
		}
		if r.For != 0 {
			return errors.Errorf("invalid field 'for' in recording rule")
		}
		if !model.IsValidMetricName(model.LabelValue(r.Record.Value)) {
			return errors.Errorf("invalid recording rule name: %s", r.Record.Value)
		}
	}

	for k, v := range r.Labels {
		if !model.LabelName(k).IsValid() || k == model.MetricNameLabel {
			return errors.Errorf("invalid label name: %s", k)
		}

		if !model.LabelValue(v).IsValid() {
			return errors.Errorf("invalid label value: %s", v)
		}
	}

	for k := range r.Annotations {
		if !model.LabelName(k).IsValid() {
			return errors.Errorf("invalid annotation name: %s", k)
		}
	}

	for _, err := range testTemplateParsing(r) {
		return err
	}

	return nil
}

// testTemplateParsing checks if the templates used in labels and annotations
// of the alerting rules are parsed correctly.
func testTemplateParsing(rl *rulefmt.RuleNode) (errs []error) {
	if rl.Alert.Value == "" {
		// Not an alerting rule.
		return errs
	}

	// Trying to parse templates.
	tmplData := template.AlertTemplateData(map[string]string{}, map[string]string{}, 0)
	defs := []string{
		"{{$labels := .Labels}}",
		"{{$externalLabels := .ExternalLabels}}",
		"{{$value := .Value}}",
	}
	parseTest := func(text string) error {
		tmpl := template.NewTemplateExpander(
			context.TODO(),
			strings.Join(append(defs, text), ""),
			"__alert_"+rl.Alert.Value,
			tmplData,
			model.Time(timestamp.FromTime(time.Now())),
			nil,
			nil,
		)
		return tmpl.ParseTest()
	}

	// Parsing Labels.
	for k, val := range rl.Labels {
		err := parseTest(val)
		if err != nil {
			errs = append(errs, errors.Wrapf(err, "label %q", k))
		}
	}

	// Parsing Annotations.
	for k, val := range rl.Annotations {
		err := parseTest(val)
		if err != nil {
			errs = append(errs, errors.Wrapf(err, "annotation %q", k))
		}
	}

	return errs
}

// Allows logql expressions to be treated as promql expressions by the prometheus rules pkg.
type exprAdapter struct {
	logql.Expr
}

func (exprAdapter) PositionRange() parser.PositionRange { return parser.PositionRange{} }
func (exprAdapter) PromQLExpr()                         {}
func (exprAdapter) Type() parser.ValueType              { return parser.ValueType("unimplemented") }
<commit_msg>exposes rule group validation fn (#2662)<commit_after>package manager

import (
	"bytes"
	"context"
	"io/ioutil"
	"strings"
	"time"

	"github.com/cortexproject/cortex/pkg/ruler"
	"github.com/go-kit/kit/log"
	"github.com/pkg/errors"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/model"
	"github.com/prometheus/prometheus/notifier"
	"github.com/prometheus/prometheus/pkg/labels"
	"github.com/prometheus/prometheus/pkg/rulefmt"
	"github.com/prometheus/prometheus/pkg/timestamp"
	"github.com/prometheus/prometheus/promql"
	"github.com/prometheus/prometheus/promql/parser"
	"github.com/prometheus/prometheus/rules"
	"github.com/prometheus/prometheus/template"
	"github.com/weaveworks/common/user"
	yaml "gopkg.in/yaml.v3"

	"github.com/grafana/loki/pkg/logproto"
	"github.com/grafana/loki/pkg/logql"
)

// engineQueryFunc returns a new query function using the rules.EngineQueryFunc function
// and passing an altered timestamp.
func engineQueryFunc(engine *logql.Engine, delay time.Duration) rules.QueryFunc {
	return rules.QueryFunc(func(ctx context.Context, qs string, t time.Time) (promql.Vector, error) {
		adjusted := t.Add(-delay)
		params := logql.NewLiteralParams(
			qs,
			adjusted,
			adjusted,
			0,
			0,
			logproto.FORWARD,
			0,
			nil,
		)
		q := engine.Query(params)

		res, err := q.Exec(ctx)
		if err != nil {
			return nil, err
		}
		switch v := res.Data.(type) {
		case promql.Vector:
			return v, nil
		case promql.Scalar:
			return promql.Vector{promql.Sample{
				Point:  promql.Point(v),
				Metric: labels.Labels{},
			}}, nil
		default:
			return nil, errors.New("rule result is not a vector or scalar")
		}
	})

}

// MultiTenantManagerAdapter will wrap a MultiTenantManager which validates loki rules
func MultiTenantManagerAdapter(mgr ruler.MultiTenantManager) *MultiTenantManager {
	return &MultiTenantManager{mgr}
}

// MultiTenantManager wraps a cortex MultiTenantManager but validates loki rules
type MultiTenantManager struct {
	ruler.MultiTenantManager
}

// ValidateRuleGroup validates a rulegroup
func (m *MultiTenantManager) ValidateRuleGroup(grp rulefmt.RuleGroup) []error {
	return ValidateGroups(grp)
}

func MemstoreTenantManager(
	cfg ruler.Config,
	engine *logql.Engine,
) ruler.ManagerFactory {
	var metrics *Metrics

	return func(
		ctx context.Context,
		userID string,
		notifier *notifier.Manager,
		logger log.Logger,
		reg prometheus.Registerer,
	) *rules.Manager {

		// We'll ignore the passed registere and use the default registerer to avoid prefix issues and other weirdness.
		// This closure prevents re-registering.
		if metrics == nil {
			metrics = NewMetrics(prometheus.DefaultRegisterer)
		}
		logger = log.With(logger, "user", userID)
		queryFunc := engineQueryFunc(engine, cfg.EvaluationDelay)
		memStore := NewMemStore(userID, queryFunc, metrics, 5*time.Minute, log.With(logger, "subcomponent", "MemStore"))

		mgr := rules.NewManager(&rules.ManagerOptions{
			Appendable:      NoopAppender{},
			Queryable:       memStore,
			QueryFunc:       queryFunc,
			Context:         user.InjectOrgID(ctx, userID),
			ExternalURL:     cfg.ExternalURL.URL,
			NotifyFunc:      ruler.SendAlerts(notifier, cfg.ExternalURL.URL.String()),
			Logger:          logger,
			Registerer:      reg,
			OutageTolerance: cfg.OutageTolerance,
			ForGracePeriod:  cfg.ForGracePeriod,
			ResendDelay:     cfg.ResendDelay,
			GroupLoader:     GroupLoader{},
		})

		// initialize memStore, bound to the manager's alerting rules
		memStore.Start(mgr)

		return mgr
	}
}

type GroupLoader struct{}

func (GroupLoader) Parse(query string) (parser.Expr, error) {
	expr, err := logql.ParseExpr(query)
	if err != nil {
		return nil, err
	}

	return exprAdapter{expr}, nil
}

func (g GroupLoader) Load(identifier string) (*rulefmt.RuleGroups, []error) {
	b, err := ioutil.ReadFile(identifier)
	if err != nil {
		return nil, []error{errors.Wrap(err, identifier)}
	}
	rgs, errs := g.parseRules(b)
	for i := range errs {
		errs[i] = errors.Wrap(errs[i], identifier)
	}
	return rgs, errs
}

func (GroupLoader) parseRules(content []byte) (*rulefmt.RuleGroups, []error) {
	var (
		groups rulefmt.RuleGroups
		errs   []error
	)

	decoder := yaml.NewDecoder(bytes.NewReader(content))
	decoder.KnownFields(true)

	if err := decoder.Decode(&groups); err != nil {
		errs = append(errs, err)
	}

	if len(errs) > 0 {
		return nil, errs
	}

	return &groups, ValidateGroups(groups.Groups...)
}

func ValidateGroups(grps ...rulefmt.RuleGroup) (errs []error) {
	set := map[string]struct{}{}

	for i, g := range grps {
		if g.Name == "" {
			errs = append(errs, errors.Errorf("group %d: Groupname must not be empty", i))
		}

		if _, ok := set[g.Name]; ok {
			errs = append(
				errs,
				errors.Errorf("groupname: \"%s\" is repeated in the same file", g.Name),
			)
		}

		set[g.Name] = struct{}{}

		for _, r := range g.Rules {
			if err := validateRuleNode(&r); err != nil {
				errs = append(errs, err)
			}
		}
	}

	return errs
}

func validateRuleNode(r *rulefmt.RuleNode) error {
	if r.Record.Value != "" && r.Alert.Value != "" {
		return errors.Errorf("only one of 'record' and 'alert' must be set")
	}

	if r.Record.Value == "" && r.Alert.Value == "" {
		return errors.Errorf("one of 'record' or 'alert' must be set")
	}

	if r.Record.Value != "" && r.Alert.Value != "" {
		return errors.Errorf("only one of 'record' or 'alert' must be set")
	}

	if r.Expr.Value == "" {
		return errors.Errorf("field 'expr' must be set in rule")
	} else if _, err := logql.ParseExpr(r.Expr.Value); err != nil {
		return errors.Wrapf(err, "could not parse expression")
	}

	if r.Record.Value != "" {
		if len(r.Annotations) > 0 {
			return errors.Errorf("invalid field 'annotations' in recording rule")
		}
		if r.For != 0 {
			return errors.Errorf("invalid field 'for' in recording rule")
		}
		if !model.IsValidMetricName(model.LabelValue(r.Record.Value)) {
			return errors.Errorf("invalid recording rule name: %s", r.Record.Value)
		}
	}

	for k, v := range r.Labels {
		if !model.LabelName(k).IsValid() || k == model.MetricNameLabel {
			return errors.Errorf("invalid label name: %s", k)
		}

		if !model.LabelValue(v).IsValid() {
			return errors.Errorf("invalid label value: %s", v)
		}
	}

	for k := range r.Annotations {
		if !model.LabelName(k).IsValid() {
			return errors.Errorf("invalid annotation name: %s", k)
		}
	}

	for _, err := range testTemplateParsing(r) {
		return err
	}

	return nil
}

// testTemplateParsing checks if the templates used in labels and annotations
// of the alerting rules are parsed correctly.
func testTemplateParsing(rl *rulefmt.RuleNode) (errs []error) {
	if rl.Alert.Value == "" {
		// Not an alerting rule.
		return errs
	}

	// Trying to parse templates.
	tmplData := template.AlertTemplateData(map[string]string{}, map[string]string{}, 0)
	defs := []string{
		"{{$labels := .Labels}}",
		"{{$externalLabels := .ExternalLabels}}",
		"{{$value := .Value}}",
	}
	parseTest := func(text string) error {
		tmpl := template.NewTemplateExpander(
			context.TODO(),
			strings.Join(append(defs, text), ""),
			"__alert_"+rl.Alert.Value,
			tmplData,
			model.Time(timestamp.FromTime(time.Now())),
			nil,
			nil,
		)
		return tmpl.ParseTest()
	}

	// Parsing Labels.
	for k, val := range rl.Labels {
		err := parseTest(val)
		if err != nil {
			errs = append(errs, errors.Wrapf(err, "label %q", k))
		}
	}

	// Parsing Annotations.
	for k, val := range rl.Annotations {
		err := parseTest(val)
		if err != nil {
			errs = append(errs, errors.Wrapf(err, "annotation %q", k))
		}
	}

	return errs
}

// Allows logql expressions to be treated as promql expressions by the prometheus rules pkg.
type exprAdapter struct {
	logql.Expr
}

func (exprAdapter) PositionRange() parser.PositionRange { return parser.PositionRange{} }
func (exprAdapter) PromQLExpr()                         {}
func (exprAdapter) Type() parser.ValueType              { return parser.ValueType("unimplemented") }
<|endoftext|>


<commit_before>// code modified from https://github.com/GoogleContainerTools/kpt/blob/master/internal/gitutil/gitutil.go

// Copyright 2019 Google LLC
//
// 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 git

import (
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/mitchellh/go-homedir"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants"
	latestV1 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest/v1"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
)

// SyncRepo syncs the target git repository with skaffold's local cache and returns the path to the repository root directory.
var SyncRepo = syncRepo
var findGit = func() (string, error) { return exec.LookPath("git") }

// defaultRef returns the default ref as "master" if master branch exists in
// remote repository, falls back to "main" if master branch doesn't exist
func defaultRef(repo string) (string, error) {
	masterRef := "master"
	mainRef := "main"
	masterExists, err := branchExists(repo, masterRef)
	if err != nil {
		return "", err
	}
	mainExists, err := branchExists(repo, mainRef)
	if err != nil {
		return "", err
	}
	if masterExists {
		return masterRef, nil
	} else if mainExists {
		return mainRef, nil
	}
	return "", fmt.Errorf("failed to get default branch for repo %s", repo)
}

// BranchExists checks if branch is present in the input repo
func branchExists(repo, branch string) (bool, error) {
	gitProgram, err := findGit()
	if err != nil {
		return false, err
	}
	out, err := util.RunCmdOut(exec.Command(gitProgram, "ls-remote", repo, branch))
	if err != nil {
		// stdErr contains the error message for os related errors, git permission errors
		// and if repo doesn't exist
		return false, fmt.Errorf("failed to lookup %s branch for repo %s: %w", branch, repo, err)
	}
	// stdOut contains the branch information if the branch is present in remote repo
	// stdOut is empty if the repo doesn't have the input branch
	if strings.TrimSpace(string(out)) != "" {
		return true, nil
	}
	return false, nil
}

// getRepoDir returns the cache directory name for a remote repo
func getRepoDir(g latestV1.GitInfo) (string, error) {
	inputs := []string{g.Repo, g.Ref}
	hasher := sha256.New()
	enc := json.NewEncoder(hasher)
	if err := enc.Encode(inputs); err != nil {
		return "", err
	}
	return base64.URLEncoding.EncodeToString(hasher.Sum(nil))[:32], nil
}

// GetRepoCacheDir returns the directory for the remote git repo cache
func GetRepoCacheDir(opts config.SkaffoldOptions) (string, error) {
	if opts.RepoCacheDir != "" {
		return opts.RepoCacheDir, nil
	}

	// cache location unspecified, use ~/.skaffold/repos
	home, err := homedir.Dir()
	if err != nil {
		return "", fmt.Errorf("retrieving home directory: %w", err)
	}
	return filepath.Join(home, constants.DefaultSkaffoldDir, "repos"), nil
}

func syncRepo(g latestV1.GitInfo, opts config.SkaffoldOptions) (string, error) {
	skaffoldCacheDir, err := GetRepoCacheDir(opts)
	r := gitCmd{Dir: skaffoldCacheDir}
	if err != nil {
		return "", fmt.Errorf("failed to clone repo %s: %w", g.Repo, err)
	}
	if err := os.MkdirAll(skaffoldCacheDir, 0700); err != nil {
		return "", fmt.Errorf(
			"failed to clone repo %s: trouble creating cache directory: %w", g.Repo, err)
	}

	ref := g.Ref
	if ref == "" {
		ref, err = defaultRef(g.Repo)
		if err != nil {
			return "", fmt.Errorf("failed to clone repo %s: trouble getting default branch: %w", g.Repo, err)
		}
	}

	hash, err := getRepoDir(g)
	if err != nil {
		return "", fmt.Errorf("failed to clone git repo: unable to create directory name: %w", err)
	}
	repoCacheDir := filepath.Join(skaffoldCacheDir, hash)
	if _, err := os.Stat(repoCacheDir); os.IsNotExist(err) {
		if _, err := r.Run("clone", g.Repo, hash, "--branch", ref, "--depth", "1"); err != nil {
			return "", fmt.Errorf("failed to clone repo: %w", err)
		}
	} else {
		r.Dir = repoCacheDir
		// check remote is defined
		if remotes, err := r.Run("remote", "-v"); err != nil {
			return "", fmt.Errorf("failed to clone repo %s: trouble checking repository remote; run 'git clone <REPO>; stat <DIR/SUBDIR>' to verify credentials: %w", g.Repo, err)
		} else if len(remotes) == 0 {
			return "", fmt.Errorf("failed to clone repo %s: remote not set for existing clone", g.Repo)
		}

		// if sync property is false, then skip fetching latest from remote and resetting the branch.
		if g.Sync != nil && !*g.Sync {
			return repoCacheDir, nil
		}

		if _, err = r.Run("fetch", "origin", ref); err != nil {
			return "", fmt.Errorf("failed to clone repo %s: unable to find any matching refs %s; run 'git clone <REPO>; stat <DIR/SUBDIR>' to verify credentials: %w", g.Repo, ref, err)
		}

		// check if the downloaded repo has uncommitted changes.
		if changes, err := r.Run("diff", "--name-only", "--ignore-submodules", "HEAD"); err != nil {
			return "", fmt.Errorf("failed to clone repo %s: unable to check for uncommitted changes; run 'git clone <REPO>; stat <DIR/SUBDIR>' to verify credentials: %w", g.Repo, err)
		} else if len(changes) > 0 {
			return "", fmt.Errorf("failed to clone repo %s: there are uncommitted changes in the target directory %s; either set the repository `sync` property to false in the skaffold config, or manually commit and sync changes to remote, or revert the local changes", g.Repo, repoCacheDir)
		}

		// check if the downloaded repo has unpushed commits.
		if changes, err := r.Run("diff", "--name-only", "--ignore-submodules", fmt.Sprintf("origin/%s...", ref)); err != nil {
			return "", fmt.Errorf("failed to clone repo %s: unable to check for unpushed commits; run 'git clone <REPO>; stat <DIR/SUBDIR>' to verify credentials: %w", g.Repo, err)
		} else if len(changes) > 0 {
			return "", fmt.Errorf("failed to clone repo %s: there are unpushed commits in the target directory %s; either set the repository `sync` property to false in the skaffold config, or manually push commits to remote, or reset the local commits", g.Repo, repoCacheDir)
		}

		// reset the repo state
		if _, err := r.Run("reset", "--hard", fmt.Sprintf("origin/%s", ref)); err != nil {
			return "", fmt.Errorf("failed to clone repo %s: trouble resetting branch to origin/%s; run 'git clone <REPO>; stat <DIR/SUBDIR>' to verify credentials: %w", g.Repo, ref, err)
		}
	}
	return repoCacheDir, nil
}

// gitCmd runs git commands in a git repo.
type gitCmd struct {
	// Dir is the directory the commands are run in.
	Dir string
}

// Run runs a git command.
// Omit the 'git' part of the command.
func (g *gitCmd) Run(args ...string) ([]byte, error) {
	p, err := findGit()
	if err != nil {
		return nil, fmt.Errorf("no 'git' program on path: %w", err)
	}

	cmd := exec.Command(p, args...)
	cmd.Dir = g.Dir
	return util.RunCmdOut(cmd)
}
<commit_msg>Use StdEncoding for git hash directory name (#6071)<commit_after>// code modified from https://github.com/GoogleContainerTools/kpt/blob/master/internal/gitutil/gitutil.go

// Copyright 2019 Google LLC
//
// 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 git

import (
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/mitchellh/go-homedir"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants"
	latestV1 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest/v1"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
)

// SyncRepo syncs the target git repository with skaffold's local cache and returns the path to the repository root directory.
var SyncRepo = syncRepo
var findGit = func() (string, error) { return exec.LookPath("git") }

// defaultRef returns the default ref as "master" if master branch exists in
// remote repository, falls back to "main" if master branch doesn't exist
func defaultRef(repo string) (string, error) {
	masterRef := "master"
	mainRef := "main"
	masterExists, err := branchExists(repo, masterRef)
	if err != nil {
		return "", err
	}
	mainExists, err := branchExists(repo, mainRef)
	if err != nil {
		return "", err
	}
	if masterExists {
		return masterRef, nil
	} else if mainExists {
		return mainRef, nil
	}
	return "", fmt.Errorf("failed to get default branch for repo %s", repo)
}

// BranchExists checks if branch is present in the input repo
func branchExists(repo, branch string) (bool, error) {
	gitProgram, err := findGit()
	if err != nil {
		return false, err
	}
	out, err := util.RunCmdOut(exec.Command(gitProgram, "ls-remote", repo, branch))
	if err != nil {
		// stdErr contains the error message for os related errors, git permission errors
		// and if repo doesn't exist
		return false, fmt.Errorf("failed to lookup %s branch for repo %s: %w", branch, repo, err)
	}
	// stdOut contains the branch information if the branch is present in remote repo
	// stdOut is empty if the repo doesn't have the input branch
	if strings.TrimSpace(string(out)) != "" {
		return true, nil
	}
	return false, nil
}

// getRepoDir returns the cache directory name for a remote repo
func getRepoDir(g latestV1.GitInfo) (string, error) {
	inputs := []string{g.Repo, g.Ref}
	hasher := sha256.New()
	enc := json.NewEncoder(hasher)
	if err := enc.Encode(inputs); err != nil {
		return "", err
	}

	// UrlEncoding supports '-' as a 63rd character, which can cause dir name issues
	return base64.StdEncoding.EncodeToString(hasher.Sum(nil))[:32], nil
}

// GetRepoCacheDir returns the directory for the remote git repo cache
func GetRepoCacheDir(opts config.SkaffoldOptions) (string, error) {
	if opts.RepoCacheDir != "" {
		return opts.RepoCacheDir, nil
	}

	// cache location unspecified, use ~/.skaffold/repos
	home, err := homedir.Dir()
	if err != nil {
		return "", fmt.Errorf("retrieving home directory: %w", err)
	}
	return filepath.Join(home, constants.DefaultSkaffoldDir, "repos"), nil
}

func syncRepo(g latestV1.GitInfo, opts config.SkaffoldOptions) (string, error) {
	skaffoldCacheDir, err := GetRepoCacheDir(opts)
	r := gitCmd{Dir: skaffoldCacheDir}
	if err != nil {
		return "", fmt.Errorf("failed to clone repo %s: %w", g.Repo, err)
	}
	if err := os.MkdirAll(skaffoldCacheDir, 0700); err != nil {
		return "", fmt.Errorf(
			"failed to clone repo %s: trouble creating cache directory: %w", g.Repo, err)
	}

	ref := g.Ref
	if ref == "" {
		ref, err = defaultRef(g.Repo)
		if err != nil {
			return "", fmt.Errorf("failed to clone repo %s: trouble getting default branch: %w", g.Repo, err)
		}
	}

	hash, err := getRepoDir(g)
	if err != nil {
		return "", fmt.Errorf("failed to clone git repo: unable to create directory name: %w", err)
	}
	repoCacheDir := filepath.Join(skaffoldCacheDir, hash)
	if _, err := os.Stat(repoCacheDir); os.IsNotExist(err) {
		if _, err := r.Run("clone", g.Repo, hash, "--branch", ref, "--depth", "1"); err != nil {
			return "", fmt.Errorf("failed to clone repo: %w", err)
		}
	} else {
		r.Dir = repoCacheDir
		// check remote is defined
		if remotes, err := r.Run("remote", "-v"); err != nil {
			return "", fmt.Errorf("failed to clone repo %s: trouble checking repository remote; run 'git clone <REPO>; stat <DIR/SUBDIR>' to verify credentials: %w", g.Repo, err)
		} else if len(remotes) == 0 {
			return "", fmt.Errorf("failed to clone repo %s: remote not set for existing clone", g.Repo)
		}

		// if sync property is false, then skip fetching latest from remote and resetting the branch.
		if g.Sync != nil && !*g.Sync {
			return repoCacheDir, nil
		}

		if _, err = r.Run("fetch", "origin", ref); err != nil {
			return "", fmt.Errorf("failed to clone repo %s: unable to find any matching refs %s; run 'git clone <REPO>; stat <DIR/SUBDIR>' to verify credentials: %w", g.Repo, ref, err)
		}

		// check if the downloaded repo has uncommitted changes.
		if changes, err := r.Run("diff", "--name-only", "--ignore-submodules", "HEAD"); err != nil {
			return "", fmt.Errorf("failed to clone repo %s: unable to check for uncommitted changes; run 'git clone <REPO>; stat <DIR/SUBDIR>' to verify credentials: %w", g.Repo, err)
		} else if len(changes) > 0 {
			return "", fmt.Errorf("failed to clone repo %s: there are uncommitted changes in the target directory %s; either set the repository `sync` property to false in the skaffold config, or manually commit and sync changes to remote, or revert the local changes", g.Repo, repoCacheDir)
		}

		// check if the downloaded repo has unpushed commits.
		if changes, err := r.Run("diff", "--name-only", "--ignore-submodules", fmt.Sprintf("origin/%s...", ref)); err != nil {
			return "", fmt.Errorf("failed to clone repo %s: unable to check for unpushed commits; run 'git clone <REPO>; stat <DIR/SUBDIR>' to verify credentials: %w", g.Repo, err)
		} else if len(changes) > 0 {
			return "", fmt.Errorf("failed to clone repo %s: there are unpushed commits in the target directory %s; either set the repository `sync` property to false in the skaffold config, or manually push commits to remote, or reset the local commits", g.Repo, repoCacheDir)
		}

		// reset the repo state
		if _, err := r.Run("reset", "--hard", fmt.Sprintf("origin/%s", ref)); err != nil {
			return "", fmt.Errorf("failed to clone repo %s: trouble resetting branch to origin/%s; run 'git clone <REPO>; stat <DIR/SUBDIR>' to verify credentials: %w", g.Repo, ref, err)
		}
	}
	return repoCacheDir, nil
}

// gitCmd runs git commands in a git repo.
type gitCmd struct {
	// Dir is the directory the commands are run in.
	Dir string
}

// Run runs a git command.
// Omit the 'git' part of the command.
func (g *gitCmd) Run(args ...string) ([]byte, error) {
	p, err := findGit()
	if err != nil {
		return nil, fmt.Errorf("no 'git' program on path: %w", err)
	}

	cmd := exec.Command(p, args...)
	cmd.Dir = g.Dir
	return util.RunCmdOut(cmd)
}
<|endoftext|>