<commit_before>package worker

import (
	"errors"
	"time"

	"code.cloudfoundry.org/clock"
	gclient "code.cloudfoundry.org/garden/client"
	gconn "code.cloudfoundry.org/garden/client/connection"
	"code.cloudfoundry.org/lager"
	"github.com/concourse/baggageclaim"
	bclient "github.com/concourse/baggageclaim/client"
	"github.com/concourse/retryhttp"

	"github.com/concourse/atc/db"
	"github.com/concourse/atc/dbng"
)

//go:generate counterfeiter . WorkerDB

type WorkerDB interface {
	Workers() ([]db.SavedWorker, error)
	GetWorker(string) (db.SavedWorker, bool, error)
	CreateContainer(container db.Container, ttl time.Duration, maxLifetime time.Duration, volumeHandles []string) (db.SavedContainer, error)
	UpdateContainerTTLToBeRemoved(container db.Container, ttl time.Duration, maxLifetime time.Duration) (db.SavedContainer, error)
	GetContainer(string) (db.SavedContainer, bool, error)
	FindContainerByIdentifier(db.ContainerIdentifier) (db.SavedContainer, bool, error)
	UpdateExpiresAtOnContainer(handle string, ttl time.Duration) error
	ReapContainer(handle string) error
	GetPipelineByID(pipelineID int) (db.SavedPipeline, error)
	ReapVolume(handle string) error
	AcquireVolumeCreatingLock(lager.Logger, int) (db.Lock, bool, error)
}

var ErrDesiredWorkerNotRunning = errors.New("desired-garden-worker-is-not-known-to-be-running")

type dbProvider struct {
	logger                    lager.Logger
	db                        WorkerDB
	dialer                    gconn.DialerFunc
	retryBackOffFactory       retryhttp.BackOffFactory
	imageFactory              ImageFactory
	dbContainerFactory        DBContainerFactory
	dbResourceCacheFactory    dbng.ResourceCacheFactory
	dbResourceTypeFactory     dbng.ResourceTypeFactory
	dbResourceConfigFactory   dbng.ResourceConfigFactory
	dbBaseResourceTypeFactory dbng.BaseResourceTypeFactory
	dbVolumeFactory           dbng.VolumeFactory
	pipelineDBFactory         db.PipelineDBFactory
	dbWorkerFactory           dbng.WorkerFactory
}

func NewDBWorkerProvider(
	logger lager.Logger,
	db WorkerDB,
	dialer gconn.DialerFunc,
	retryBackOffFactory retryhttp.BackOffFactory,
	imageFactory ImageFactory,
	dbContainerFactory DBContainerFactory,
	dbResourceCacheFactory dbng.ResourceCacheFactory,
	dbResourceConfigFactory dbng.ResourceConfigFactory,
	dbBaseResourceTypeFactory dbng.BaseResourceTypeFactory,
	dbVolumeFactory dbng.VolumeFactory,
	pipelineDBFactory db.PipelineDBFactory,
	workerFactory dbng.WorkerFactory,
) WorkerProvider {
	return &dbProvider{
		logger:                    logger,
		db:                        db,
		dialer:                    dialer,
		retryBackOffFactory:       retryBackOffFactory,
		imageFactory:              imageFactory,
		dbContainerFactory:        dbContainerFactory,
		dbResourceCacheFactory:    dbResourceCacheFactory,
		dbResourceConfigFactory:   dbResourceConfigFactory,
		dbBaseResourceTypeFactory: dbBaseResourceTypeFactory,
		dbVolumeFactory:           dbVolumeFactory,
		dbWorkerFactory:           workerFactory,
		pipelineDBFactory:         pipelineDBFactory,
	}
}

func (provider *dbProvider) Workers() ([]Worker, error) {
	savedWorkers, err := provider.dbWorkerFactory.Workers()
	if err != nil {
		return nil, err
	}

	tikTok := clock.NewClock()

	workers := []Worker{}

	for _, savedWorker := range savedWorkers {
		if savedWorker.State == dbng.WorkerStateRunning {
			workers = append(workers, provider.newGardenWorker(tikTok, savedWorker))
		}
	}

	return workers, nil
}

func (provider *dbProvider) GetWorker(name string) (Worker, bool, error) {
	savedWorker, found, err := provider.dbWorkerFactory.GetWorker(name)
	if err != nil {
		return nil, false, err
	}

	if !found {
		return nil, false, nil
	}

	if savedWorker.State != dbng.WorkerStateRunning {
		return nil, true, ErrDesiredWorkerNotRunning
	}

	tikTok := clock.NewClock()

	worker := provider.newGardenWorker(tikTok, savedWorker)

	return worker, found, nil
}

func (provider *dbProvider) FindContainerForIdentifier(id Identifier) (db.SavedContainer, bool, error) {
	return provider.db.FindContainerByIdentifier(db.ContainerIdentifier(id))
}

func (provider *dbProvider) GetContainer(handle string) (db.SavedContainer, bool, error) {
	return provider.db.GetContainer(handle)
}

func (provider *dbProvider) ReapContainer(handle string) error {
	return provider.db.ReapContainer(handle)
}

func (provider *dbProvider) newGardenWorker(tikTok clock.Clock, savedWorker *dbng.Worker) Worker {
	gcf := NewGardenConnectionFactory(
		provider.dbWorkerFactory,
		provider.logger.Session("garden-connection"),
		savedWorker.Name,
		*(savedWorker.GardenAddr),
		provider.retryBackOffFactory,
	)

	connection := NewRetryableConnection(gcf.BuildConnection())

	var bClient baggageclaim.Client
	if savedWorker.BaggageclaimURL != "" {
		bClient = bclient.New(savedWorker.BaggageclaimURL)
	}

	volumeFactory := NewVolumeFactory(provider.db)

	volumeClient := NewVolumeClient(
		bClient,
		provider.db,
		volumeFactory,
		provider.dbVolumeFactory,
		provider.dbBaseResourceTypeFactory,
		clock.NewClock(),
		&dbng.Worker{
			Name:       savedWorker.Name,
			GardenAddr: &savedWorker.GardenAddr,
		},
	)

	return NewGardenWorker(
		gclient.New(connection),
		bClient,
		volumeClient,
		volumeFactory,
		provider.imageFactory,
		provider.pipelineDBFactory,
		provider.dbContainerFactory,
		provider.dbVolumeFactory,
		provider.dbResourceCacheFactory,
		provider.dbResourceConfigFactory,
		provider.db,
		provider,
		tikTok,
		savedWorker.ActiveContainers,
		savedWorker.ResourceTypes,
		savedWorker.Platform,
		savedWorker.Tags,
		savedWorker.TeamID,
		savedWorker.Name,
		*savedWorker.GardenAddr,
		savedWorker.StartTime,
		savedWorker.HTTPProxyURL,
		savedWorker.HTTPSProxyURL,
		savedWorker.NoProxy,
	)
}
<commit_msg>Remove unnecessary pointer reference of GardenAddr [#129726203]<commit_after>package worker

import (
	"errors"
	"time"

	"code.cloudfoundry.org/clock"
	gclient "code.cloudfoundry.org/garden/client"
	gconn "code.cloudfoundry.org/garden/client/connection"
	"code.cloudfoundry.org/lager"
	"github.com/concourse/baggageclaim"
	bclient "github.com/concourse/baggageclaim/client"
	"github.com/concourse/retryhttp"

	"github.com/concourse/atc/db"
	"github.com/concourse/atc/dbng"
)

//go:generate counterfeiter . WorkerDB

type WorkerDB interface {
	Workers() ([]db.SavedWorker, error)
	GetWorker(string) (db.SavedWorker, bool, error)
	CreateContainer(container db.Container, ttl time.Duration, maxLifetime time.Duration, volumeHandles []string) (db.SavedContainer, error)
	UpdateContainerTTLToBeRemoved(container db.Container, ttl time.Duration, maxLifetime time.Duration) (db.SavedContainer, error)
	GetContainer(string) (db.SavedContainer, bool, error)
	FindContainerByIdentifier(db.ContainerIdentifier) (db.SavedContainer, bool, error)
	UpdateExpiresAtOnContainer(handle string, ttl time.Duration) error
	ReapContainer(handle string) error
	GetPipelineByID(pipelineID int) (db.SavedPipeline, error)
	ReapVolume(handle string) error
	AcquireVolumeCreatingLock(lager.Logger, int) (db.Lock, bool, error)
}

var ErrDesiredWorkerNotRunning = errors.New("desired-garden-worker-is-not-known-to-be-running")

type dbProvider struct {
	logger                    lager.Logger
	db                        WorkerDB
	dialer                    gconn.DialerFunc
	retryBackOffFactory       retryhttp.BackOffFactory
	imageFactory              ImageFactory
	dbContainerFactory        DBContainerFactory
	dbResourceCacheFactory    dbng.ResourceCacheFactory
	dbResourceTypeFactory     dbng.ResourceTypeFactory
	dbResourceConfigFactory   dbng.ResourceConfigFactory
	dbBaseResourceTypeFactory dbng.BaseResourceTypeFactory
	dbVolumeFactory           dbng.VolumeFactory
	pipelineDBFactory         db.PipelineDBFactory
	dbWorkerFactory           dbng.WorkerFactory
}

func NewDBWorkerProvider(
	logger lager.Logger,
	db WorkerDB,
	dialer gconn.DialerFunc,
	retryBackOffFactory retryhttp.BackOffFactory,
	imageFactory ImageFactory,
	dbContainerFactory DBContainerFactory,
	dbResourceCacheFactory dbng.ResourceCacheFactory,
	dbResourceConfigFactory dbng.ResourceConfigFactory,
	dbBaseResourceTypeFactory dbng.BaseResourceTypeFactory,
	dbVolumeFactory dbng.VolumeFactory,
	pipelineDBFactory db.PipelineDBFactory,
	workerFactory dbng.WorkerFactory,
) WorkerProvider {
	return &dbProvider{
		logger:                    logger,
		db:                        db,
		dialer:                    dialer,
		retryBackOffFactory:       retryBackOffFactory,
		imageFactory:              imageFactory,
		dbContainerFactory:        dbContainerFactory,
		dbResourceCacheFactory:    dbResourceCacheFactory,
		dbResourceConfigFactory:   dbResourceConfigFactory,
		dbBaseResourceTypeFactory: dbBaseResourceTypeFactory,
		dbVolumeFactory:           dbVolumeFactory,
		dbWorkerFactory:           workerFactory,
		pipelineDBFactory:         pipelineDBFactory,
	}
}

func (provider *dbProvider) Workers() ([]Worker, error) {
	savedWorkers, err := provider.dbWorkerFactory.Workers()
	if err != nil {
		return nil, err
	}

	tikTok := clock.NewClock()

	workers := []Worker{}

	for _, savedWorker := range savedWorkers {
		if savedWorker.State == dbng.WorkerStateRunning {
			workers = append(workers, provider.newGardenWorker(tikTok, savedWorker))
		}
	}

	return workers, nil
}

func (provider *dbProvider) GetWorker(name string) (Worker, bool, error) {
	savedWorker, found, err := provider.dbWorkerFactory.GetWorker(name)
	if err != nil {
		return nil, false, err
	}

	if !found {
		return nil, false, nil
	}

	if savedWorker.State != dbng.WorkerStateRunning {
		return nil, true, ErrDesiredWorkerNotRunning
	}

	tikTok := clock.NewClock()

	worker := provider.newGardenWorker(tikTok, savedWorker)

	return worker, found, nil
}

func (provider *dbProvider) FindContainerForIdentifier(id Identifier) (db.SavedContainer, bool, error) {
	return provider.db.FindContainerByIdentifier(db.ContainerIdentifier(id))
}

func (provider *dbProvider) GetContainer(handle string) (db.SavedContainer, bool, error) {
	return provider.db.GetContainer(handle)
}

func (provider *dbProvider) ReapContainer(handle string) error {
	return provider.db.ReapContainer(handle)
}

func (provider *dbProvider) newGardenWorker(tikTok clock.Clock, savedWorker *dbng.Worker) Worker {
	gcf := NewGardenConnectionFactory(
		provider.dbWorkerFactory,
		provider.logger.Session("garden-connection"),
		savedWorker.Name,
		*(savedWorker.GardenAddr),
		provider.retryBackOffFactory,
	)

	connection := NewRetryableConnection(gcf.BuildConnection())

	var bClient baggageclaim.Client
	if savedWorker.BaggageclaimURL != "" {
		bClient = bclient.New(savedWorker.BaggageclaimURL)
	}

	volumeFactory := NewVolumeFactory(provider.db)

	volumeClient := NewVolumeClient(
		bClient,
		provider.db,
		volumeFactory,
		provider.dbVolumeFactory,
		provider.dbBaseResourceTypeFactory,
		clock.NewClock(),
		&dbng.Worker{
			Name:       savedWorker.Name,
			GardenAddr: savedWorker.GardenAddr,
		},
	)

	return NewGardenWorker(
		gclient.New(connection),
		bClient,
		volumeClient,
		volumeFactory,
		provider.imageFactory,
		provider.pipelineDBFactory,
		provider.dbContainerFactory,
		provider.dbVolumeFactory,
		provider.dbResourceCacheFactory,
		provider.dbResourceConfigFactory,
		provider.db,
		provider,
		tikTok,
		savedWorker.ActiveContainers,
		savedWorker.ResourceTypes,
		savedWorker.Platform,
		savedWorker.Tags,
		savedWorker.TeamID,
		savedWorker.Name,
		*savedWorker.GardenAddr,
		savedWorker.StartTime,
		savedWorker.HTTPProxyURL,
		savedWorker.HTTPSProxyURL,
		savedWorker.NoProxy,
	)
}
<|endoftext|>


<commit_before>// Copyright 2016, RadiantBlue Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package workflow

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strings"

	"github.com/venicegeo/pz-gocommon/elasticsearch"
	"github.com/venicegeo/pz-gocommon/gocommon"
)

type TriggerDB struct {
	*ResourceDB
	mapping string
}

func NewTriggerDB(service *Service, esi elasticsearch.IIndex) (*TriggerDB, error) {

	rdb, err := NewResourceDB(service, esi, TriggerIndexSettings)
	if err != nil {
		return nil, err
	}
	ardb := TriggerDB{ResourceDB: rdb, mapping: TriggerDBMapping}
	return &ardb, nil
}

func (db *TriggerDB) PostTrigger(trigger *Trigger, id piazza.Ident) (piazza.Ident, error) {

	{ //CHECK SERVICE EXISTS
		jobData := trigger.Job.JobType.Data
		serviceID := jobData["serviceId"]
		strServiceID, ok := serviceID.(string)
		if !ok {
			return piazza.NoIdent, LoggedError("TriggerDB.PostData failed: serviceId field not of type string")
		}
		serviceControllerURL, err := db.service.sys.GetURL("pz-servicecontroller")
		if err == nil {
			// TODO:
			// if err is nil, we have a servicecontroller to talk to
			// if err is not nil, we'll assume we are mocking (which means
			// we have no servicecontroller client to mock)
			response, err := http.Get(serviceControllerURL)
			if err != nil {
				return piazza.NoIdent, LoggedError("TriggerDB.PostData failed to make request to ServiceController: %s", err)
			}
			if response.StatusCode != 200 {
				return piazza.NoIdent, LoggedError("TriggerDB.PostData failed: serviceID %s does not exist", strServiceID)
			}
		}
	}
	{ //CHECK EVENTTYPE IDS
		if len(trigger.Condition.EventTypeIDs) == 0 {
			return piazza.NoIdent, LoggedError("TriggerDB.PostData failed: no eventTypeIds were specified")
		}
		for _, id := range trigger.Condition.EventTypeIDs {
			_, found, err := db.service.eventTypeDB.GetOne(id)
			if !found || err != nil {
				return piazza.NoIdent, LoggedError("TriggerDB.PostData failed: eventType %s could not be found", id.String())
			}
		}
	}

	ifaceObj := trigger.Condition.Query
	//log.Printf("Query: %v", ifaceObj)
	body, err := json.Marshal(ifaceObj)
	if err != nil {
		return piazza.NoIdent, err
	}

	jsn := string(body)
	//log.Printf("Current json: %s", jsn)
	// Remove trailing }
	jsn = jsn[:len(jsn)-1]
	jsn += ",\"type\":["
	// Add the types that the percolation query can match
	for _, id := range trigger.Condition.EventTypeIDs {
		jsn += fmt.Sprintf("\"%s\",", id)
	}
	jsn = jsn[:len(jsn)-1]
	// Add back trailing } and ] to close array
	jsn += "]}"

	//log.Printf("Posting percolation query: %s", body)
	indexResult, err := db.service.eventDB.Esi.AddPercolationQuery(string(trigger.TriggerID), piazza.JsonString(body))
	if err != nil {
		var errMessage string
		if strings.Contains(err.Error(), "elastic: Error 500 (Internal Server Error): failed to parse query") {
			errMessage = fmt.Sprintf("TriggerDB.PostData addpercquery failed: elastic failed to parse query. Common causes: [Variables do not start with 'data.' or are not found at your specified path, invalid perc query structure].")
		} else {
			errMessage = fmt.Sprintf("TriggerDB.PostData addpercquery failed: %s [unknown cause]", err)
		}
		return piazza.NoIdent, LoggedError(errMessage)
	}
	if indexResult == nil {
		return piazza.NoIdent, LoggedError("TriggerDB.PostData addpercquery failed: no indexResult")
	}
	if !indexResult.Created {
		return piazza.NoIdent, LoggedError("TriggerDB.PostData addpercquery failed: not created")
	}

	//log.Printf("percolation query added: ID: %s, Type: %s, Index: %s", indexResult.Id, indexResult.Type, indexResult.Index)
	//log.Printf("percolation id: %s", indexResult.Id)
	trigger.PercolationID = piazza.Ident(indexResult.ID)

	strTrigger, err := piazza.StructInterfaceToString(trigger)
	if err != nil {
		_, _ = db.service.eventDB.Esi.DeletePercolationQuery(trigger.TriggerID.String())
		return piazza.NoIdent, LoggedError("TriggerDB.PostData failed: %s", err)
	}
	intTrigger, err := piazza.StructStringToInterface(strTrigger)
	if err != nil {
		_, _ = db.service.eventDB.Esi.DeletePercolationQuery(trigger.TriggerID.String())
		return piazza.NoIdent, LoggedError("TriggerDB.PostData failed: %s", err)
	}
	mapTrigger, ok := intTrigger.(map[string]interface{})
	if !ok {
		_, _ = db.service.eventDB.Esi.DeletePercolationQuery(trigger.TriggerID.String())
		return piazza.NoIdent, LoggedError("TriggerDB.PostData failed: bad trigger")
	}
	fixedTrigger := fixTrigger(mapTrigger)

	indexResult2, err := db.Esi.PostData(db.mapping, id.String(), fixedTrigger)
	if err != nil {
		_, _ = db.service.eventDB.Esi.DeletePercolationQuery(string(trigger.TriggerID))
		return piazza.NoIdent, LoggedError("TriggerDB.PostData failed: %s", err)
	}
	if !indexResult2.Created {
		_, _ = db.service.eventDB.Esi.DeletePercolationQuery(string(trigger.TriggerID))
		return piazza.NoIdent, LoggedError("TriggerDB.PostData failed: not created")
	}

	return id, nil
}

func (db *TriggerDB) PutTrigger(id piazza.Ident, trigger *Trigger, update *TriggerUpdate) (*Trigger, error) {
	trigger.Enabled = update.Enabled
	_, err := db.Esi.PutData(db.mapping, id.String(), trigger)
	if err != nil {
		return trigger, LoggedError("TriggerDB.PutData failed: %s", err)
	}
	return trigger, nil
}

func (db *TriggerDB) GetAll(format *piazza.JsonPagination) ([]Trigger, int64, error) {
	triggers := []Trigger{}

	exists, err := db.Esi.TypeExists(db.mapping)
	if err != nil {
		return triggers, 0, err
	}
	if !exists {
		return triggers, 0, nil
	}

	searchResult, err := db.Esi.FilterByMatchAll(db.mapping, format)
	if err != nil {
		return nil, 0, LoggedError("TriggerDB.GetAll failed: %s", err)
	}
	if searchResult == nil {
		return nil, 0, LoggedError("TriggerDB.GetAll failed: no searchResult")
	}

	if searchResult != nil && searchResult.GetHits() != nil {

		for _, hit := range *searchResult.GetHits() {
			var trigger Trigger
			err := json.Unmarshal(*hit.Source, &trigger)
			if err != nil {
				return nil, 0, err
			}
			triggers = append(triggers, trigger)
		}
	}
	return triggers, searchResult.TotalHits(), nil
}

func (db *TriggerDB) GetOne(id piazza.Ident) (*Trigger, bool, error) {

	getResult, err := db.Esi.GetByID(db.mapping, id.String())
	if err != nil {
		return nil, getResult.Found, LoggedError("TriggerDB.GetOne failed: %s", err)
	}
	if getResult == nil {
		return nil, true, LoggedError("TriggerDB.GetOne failed: no getResult")
	}

	src := getResult.Source
	var obj Trigger
	err = json.Unmarshal(*src, &obj)
	if err != nil {
		return nil, getResult.Found, err
	}

	return &obj, getResult.Found, nil
}

func (db *TriggerDB) GetTriggersByEventTypeID(id piazza.Ident) ([]Trigger, int64, error) {
	triggers := []Trigger{}

	exists, err := db.Esi.TypeExists(db.mapping)
	if err != nil {
		return triggers, 0, err
	}
	if !exists {
		return triggers, 0, nil
	}

	searchResult, err := db.Esi.FilterByTermQuery(db.mapping, "condition.eventTypeIds", id)
	if err != nil {
		return nil, 0, LoggedError("TriggerDB.GetTriggersByEventTypeId failed: %s", err)
	}
	if searchResult == nil {
		return nil, 0, LoggedError("TriggerDB.GetTriggersByEventTypeId failed: no searchResult")
	}

	if searchResult != nil && searchResult.GetHits() != nil {

		for _, hit := range *searchResult.GetHits() {
			var trigger Trigger
			err := json.Unmarshal(*hit.Source, &trigger)
			if err != nil {
				return nil, 0, err
			}
			triggers = append(triggers, trigger)
		}
	}
	return triggers, searchResult.TotalHits(), nil
}

func (db *TriggerDB) DeleteTrigger(id piazza.Ident) (bool, error) {

	trigger, found, err := db.GetOne(id)
	if err != nil {
		return found, err
	}
	if trigger == nil {
		return false, nil
	}

	deleteResult, err := db.Esi.DeleteByID(db.mapping, string(id))
	if err != nil {
		return deleteResult.Found, LoggedError("TriggerDB.DeleteById failed: %s", err)
	}
	if deleteResult == nil {
		return false, LoggedError("TriggerDB.DeleteById failed: no deleteResult")
	}
	if !deleteResult.Found {
		return false, nil
	}

	deleteResult2, err := db.service.eventDB.Esi.DeletePercolationQuery(string(trigger.PercolationID))
	if err != nil {
		return deleteResult2.Found, LoggedError("TriggerDB.DeleteById percquery failed: %s", err)
	}
	if deleteResult2 == nil {
		return false, LoggedError("TriggerDB.DeleteById percquery failed: no deleteResult")
	}

	return deleteResult2.Found, nil
}

func fixTrigger(input interface{}) interface{} {
	var output interface{}
	switch input.(type) {
	case map[string]interface{}:
		output = fixTriggerNodeMap(input.(map[string]interface{}))
	case []interface{}:
		output = fixTriggerNodeArr(input.([]interface{}))
	}
	return output
}
func fixTriggerNodeMap(inputObj map[string]interface{}) map[string]interface{} {
	outputObj := map[string]interface{}{}
	for k, v := range inputObj {
		switch v.(type) {
		case []interface{}:
			outputObj[strings.Replace(k, ".", "~", -1)] = fixTriggerNodeArr(v.([]interface{}))
		case map[string]interface{}:
			outputObj[strings.Replace(k, ".", "~", -1)] = fixTriggerNodeMap(v.(map[string]interface{}))
		default:
			outputObj[strings.Replace(k, ".", "~", -1)] = v
		}
	}
	return outputObj
}

func fixTriggerNodeArr(inputObj []interface{}) []interface{} {
	outputObj := []interface{}{}
	for _, v := range inputObj {
		switch v.(type) {
		case []interface{}:
			outputObj = append(outputObj, fixTriggerNodeArr(v.([]interface{})))
		case map[string]interface{}:
			outputObj = append(outputObj, fixTriggerNodeMap(v.(map[string]interface{})))
		default:
			outputObj = append(outputObj, v)
		}
	}
	return outputObj
}
<commit_msg>Actually checks the service id this time when posting a trigger<commit_after>// Copyright 2016, RadiantBlue Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package workflow

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strings"

	"github.com/venicegeo/pz-gocommon/elasticsearch"
	"github.com/venicegeo/pz-gocommon/gocommon"
)

type TriggerDB struct {
	*ResourceDB
	mapping string
}

func NewTriggerDB(service *Service, esi elasticsearch.IIndex) (*TriggerDB, error) {

	rdb, err := NewResourceDB(service, esi, TriggerIndexSettings)
	if err != nil {
		return nil, err
	}
	ardb := TriggerDB{ResourceDB: rdb, mapping: TriggerDBMapping}
	return &ardb, nil
}

func (db *TriggerDB) PostTrigger(trigger *Trigger, id piazza.Ident) (piazza.Ident, error) {
	{ //CHECK SERVICE EXISTS
		serviceID := trigger.Job.JobType.Data["serviceId"]
		strServiceID, ok := serviceID.(string)
		if !ok {
			return piazza.NoIdent, LoggedError("TriggerDB.PostData failed: serviceId field not of type string")
		}
		serviceControllerURL, err := db.service.sys.GetURL("pz-servicecontroller")
		if err == nil {
			// TODO:
			// if err is nil, we have a servicecontroller to talk to
			// if err is not nil, we'll assume we are mocking (which means
			// we have no servicecontroller client to mock)
			response, err := http.Get(fmt.Sprintf("%s/service/%s", serviceControllerURL, strServiceID))
			if err != nil {
				return piazza.NoIdent, LoggedError("TriggerDB.PostData failed to make request to ServiceController: %s", err)
			}
			if response.StatusCode != 200 {
				return piazza.NoIdent, LoggedError("TriggerDB.PostData failed: serviceID %s does not exist", strServiceID)
			}
		}
	}
	{ //CHECK EVENTTYPE IDS
		if len(trigger.Condition.EventTypeIDs) == 0 {
			return piazza.NoIdent, LoggedError("TriggerDB.PostData failed: no eventTypeIds were specified")
		}
		for _, id := range trigger.Condition.EventTypeIDs {
			_, found, err := db.service.eventTypeDB.GetOne(id)
			if !found || err != nil {
				return piazza.NoIdent, LoggedError("TriggerDB.PostData failed: eventType %s could not be found", id.String())
			}
		}
	}

	ifaceObj := trigger.Condition.Query
	//log.Printf("Query: %v", ifaceObj)
	body, err := json.Marshal(ifaceObj)
	if err != nil {
		return piazza.NoIdent, err
	}

	jsn := string(body)
	//log.Printf("Current json: %s", jsn)
	// Remove trailing }
	jsn = jsn[:len(jsn)-1]
	jsn += ",\"type\":["
	// Add the types that the percolation query can match
	for _, id := range trigger.Condition.EventTypeIDs {
		jsn += fmt.Sprintf("\"%s\",", id)
	}
	jsn = jsn[:len(jsn)-1]
	// Add back trailing } and ] to close array
	jsn += "]}"

	//log.Printf("Posting percolation query: %s", body)
	indexResult, err := db.service.eventDB.Esi.AddPercolationQuery(string(trigger.TriggerID), piazza.JsonString(body))
	if err != nil {
		var errMessage string
		if strings.Contains(err.Error(), "elastic: Error 500 (Internal Server Error): failed to parse query") {
			errMessage = fmt.Sprintf("TriggerDB.PostData addpercquery failed: elastic failed to parse query. Common causes: [Variables do not start with 'data.' or are not found at your specified path, invalid perc query structure].")
		} else {
			errMessage = fmt.Sprintf("TriggerDB.PostData addpercquery failed: %s [unknown cause]", err)
		}
		return piazza.NoIdent, LoggedError(errMessage)
	}
	if indexResult == nil {
		return piazza.NoIdent, LoggedError("TriggerDB.PostData addpercquery failed: no indexResult")
	}
	if !indexResult.Created {
		return piazza.NoIdent, LoggedError("TriggerDB.PostData addpercquery failed: not created")
	}

	//log.Printf("percolation query added: ID: %s, Type: %s, Index: %s", indexResult.Id, indexResult.Type, indexResult.Index)
	//log.Printf("percolation id: %s", indexResult.Id)
	trigger.PercolationID = piazza.Ident(indexResult.ID)

	strTrigger, err := piazza.StructInterfaceToString(trigger)
	if err != nil {
		_, _ = db.service.eventDB.Esi.DeletePercolationQuery(trigger.TriggerID.String())
		return piazza.NoIdent, LoggedError("TriggerDB.PostData failed: %s", err)
	}
	intTrigger, err := piazza.StructStringToInterface(strTrigger)
	if err != nil {
		_, _ = db.service.eventDB.Esi.DeletePercolationQuery(trigger.TriggerID.String())
		return piazza.NoIdent, LoggedError("TriggerDB.PostData failed: %s", err)
	}
	mapTrigger, ok := intTrigger.(map[string]interface{})
	if !ok {
		_, _ = db.service.eventDB.Esi.DeletePercolationQuery(trigger.TriggerID.String())
		return piazza.NoIdent, LoggedError("TriggerDB.PostData failed: bad trigger")
	}
	fixedTrigger := fixTrigger(mapTrigger)

	indexResult2, err := db.Esi.PostData(db.mapping, id.String(), fixedTrigger)
	if err != nil {
		_, _ = db.service.eventDB.Esi.DeletePercolationQuery(string(trigger.TriggerID))
		return piazza.NoIdent, LoggedError("TriggerDB.PostData failed: %s", err)
	}
	if !indexResult2.Created {
		_, _ = db.service.eventDB.Esi.DeletePercolationQuery(string(trigger.TriggerID))
		return piazza.NoIdent, LoggedError("TriggerDB.PostData failed: not created")
	}

	return id, nil
}

func (db *TriggerDB) PutTrigger(id piazza.Ident, trigger *Trigger, update *TriggerUpdate) (*Trigger, error) {
	trigger.Enabled = update.Enabled
	_, err := db.Esi.PutData(db.mapping, id.String(), trigger)
	if err != nil {
		return trigger, LoggedError("TriggerDB.PutData failed: %s", err)
	}
	return trigger, nil
}

func (db *TriggerDB) GetAll(format *piazza.JsonPagination) ([]Trigger, int64, error) {
	triggers := []Trigger{}

	exists, err := db.Esi.TypeExists(db.mapping)
	if err != nil {
		return triggers, 0, err
	}
	if !exists {
		return triggers, 0, nil
	}

	searchResult, err := db.Esi.FilterByMatchAll(db.mapping, format)
	if err != nil {
		return nil, 0, LoggedError("TriggerDB.GetAll failed: %s", err)
	}
	if searchResult == nil {
		return nil, 0, LoggedError("TriggerDB.GetAll failed: no searchResult")
	}

	if searchResult != nil && searchResult.GetHits() != nil {

		for _, hit := range *searchResult.GetHits() {
			var trigger Trigger
			err := json.Unmarshal(*hit.Source, &trigger)
			if err != nil {
				return nil, 0, err
			}
			triggers = append(triggers, trigger)
		}
	}
	return triggers, searchResult.TotalHits(), nil
}

func (db *TriggerDB) GetOne(id piazza.Ident) (*Trigger, bool, error) {

	getResult, err := db.Esi.GetByID(db.mapping, id.String())
	if err != nil {
		return nil, getResult.Found, LoggedError("TriggerDB.GetOne failed: %s", err)
	}
	if getResult == nil {
		return nil, true, LoggedError("TriggerDB.GetOne failed: no getResult")
	}

	src := getResult.Source
	var obj Trigger
	err = json.Unmarshal(*src, &obj)
	if err != nil {
		return nil, getResult.Found, err
	}

	return &obj, getResult.Found, nil
}

func (db *TriggerDB) GetTriggersByEventTypeID(id piazza.Ident) ([]Trigger, int64, error) {
	triggers := []Trigger{}

	exists, err := db.Esi.TypeExists(db.mapping)
	if err != nil {
		return triggers, 0, err
	}
	if !exists {
		return triggers, 0, nil
	}

	searchResult, err := db.Esi.FilterByTermQuery(db.mapping, "condition.eventTypeIds", id)
	if err != nil {
		return nil, 0, LoggedError("TriggerDB.GetTriggersByEventTypeId failed: %s", err)
	}
	if searchResult == nil {
		return nil, 0, LoggedError("TriggerDB.GetTriggersByEventTypeId failed: no searchResult")
	}

	if searchResult != nil && searchResult.GetHits() != nil {

		for _, hit := range *searchResult.GetHits() {
			var trigger Trigger
			err := json.Unmarshal(*hit.Source, &trigger)
			if err != nil {
				return nil, 0, err
			}
			triggers = append(triggers, trigger)
		}
	}
	return triggers, searchResult.TotalHits(), nil
}

func (db *TriggerDB) DeleteTrigger(id piazza.Ident) (bool, error) {

	trigger, found, err := db.GetOne(id)
	if err != nil {
		return found, err
	}
	if trigger == nil {
		return false, nil
	}

	deleteResult, err := db.Esi.DeleteByID(db.mapping, string(id))
	if err != nil {
		return deleteResult.Found, LoggedError("TriggerDB.DeleteById failed: %s", err)
	}
	if deleteResult == nil {
		return false, LoggedError("TriggerDB.DeleteById failed: no deleteResult")
	}
	if !deleteResult.Found {
		return false, nil
	}

	deleteResult2, err := db.service.eventDB.Esi.DeletePercolationQuery(string(trigger.PercolationID))
	if err != nil {
		return deleteResult2.Found, LoggedError("TriggerDB.DeleteById percquery failed: %s", err)
	}
	if deleteResult2 == nil {
		return false, LoggedError("TriggerDB.DeleteById percquery failed: no deleteResult")
	}

	return deleteResult2.Found, nil
}

func fixTrigger(input interface{}) interface{} {
	var output interface{}
	switch input.(type) {
	case map[string]interface{}:
		output = fixTriggerNodeMap(input.(map[string]interface{}))
	case []interface{}:
		output = fixTriggerNodeArr(input.([]interface{}))
	}
	return output
}
func fixTriggerNodeMap(inputObj map[string]interface{}) map[string]interface{} {
	outputObj := map[string]interface{}{}
	for k, v := range inputObj {
		switch v.(type) {
		case []interface{}:
			outputObj[strings.Replace(k, ".", "~", -1)] = fixTriggerNodeArr(v.([]interface{}))
		case map[string]interface{}:
			outputObj[strings.Replace(k, ".", "~", -1)] = fixTriggerNodeMap(v.(map[string]interface{}))
		default:
			outputObj[strings.Replace(k, ".", "~", -1)] = v
		}
	}
	return outputObj
}

func fixTriggerNodeArr(inputObj []interface{}) []interface{} {
	outputObj := []interface{}{}
	for _, v := range inputObj {
		switch v.(type) {
		case []interface{}:
			outputObj = append(outputObj, fixTriggerNodeArr(v.([]interface{})))
		case map[string]interface{}:
			outputObj = append(outputObj, fixTriggerNodeMap(v.(map[string]interface{})))
		default:
			outputObj = append(outputObj, v)
		}
	}
	return outputObj
}
<|endoftext|>


<commit_before>// Copyright 2016 The etcd 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 e2e

import (
	"strings"
	"testing"
)

func TestCtlV3Alarm(t *testing.T) { testCtl(t, alarmTest, withQuota(64*1024)) }

func alarmTest(cx ctlCtx) {
	// test small put still works
	smallbuf := strings.Repeat("a", int(cx.quotaBackendBytes/100))
	if err := ctlV3Put(cx, "abc", smallbuf, ""); err != nil {
		cx.t.Fatal(err)
	}

	// test big put (to be rejected, and trigger quota alarm)
	bigbuf := strings.Repeat("a", int(cx.quotaBackendBytes))
	if err := ctlV3Put(cx, "abc", bigbuf, ""); err != nil {
		if !strings.Contains(err.Error(), "etcdserver: mvcc: database space exceeded") {
			cx.t.Fatal(err)
		}
	}
	if err := ctlV3Alarm(cx, "list", "alarm:NOSPACE"); err != nil {
		cx.t.Fatal(err)
	}

	// alarm is on rejecting Puts and Txns
	if err := ctlV3Put(cx, "def", smallbuf, ""); err != nil {
		if !strings.Contains(err.Error(), "etcdserver: mvcc: database space exceeded") {
			cx.t.Fatal(err)
		}
	}

	// turn off alarm
	if err := ctlV3Alarm(cx, "disarm", "alarm:NOSPACE"); err != nil {
		cx.t.Fatal(err)
	}

	// put one more key below quota
	if err := ctlV3Put(cx, "ghi", smallbuf, ""); err != nil {
		cx.t.Fatal(err)
	}
}

func ctlV3Alarm(cx ctlCtx, cmd string, as ...string) error {
	cmdArgs := append(cx.PrefixArgs(), "alarm", cmd)
	return spawnWithExpects(cmdArgs, as...)
}
<commit_msg>e2e/ctl_v3_alarm_test: Use fixed small buf size<commit_after>// Copyright 2016 The etcd 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 e2e

import (
	"strings"
	"testing"
)

func TestCtlV3Alarm(t *testing.T) { testCtl(t, alarmTest, withQuota(64*1024)) }

func alarmTest(cx ctlCtx) {
	// test small put still works
	smallbuf := strings.Repeat("a", 64)
	if err := ctlV3Put(cx, "abc", smallbuf, ""); err != nil {
		cx.t.Fatal(err)
	}

	// test big put (to be rejected, and trigger quota alarm)
	bigbuf := strings.Repeat("a", int(cx.quotaBackendBytes))
	if err := ctlV3Put(cx, "abc", bigbuf, ""); err != nil {
		if !strings.Contains(err.Error(), "etcdserver: mvcc: database space exceeded") {
			cx.t.Fatal(err)
		}
	}
	if err := ctlV3Alarm(cx, "list", "alarm:NOSPACE"); err != nil {
		cx.t.Fatal(err)
	}

	// alarm is on rejecting Puts and Txns
	if err := ctlV3Put(cx, "def", smallbuf, ""); err != nil {
		if !strings.Contains(err.Error(), "etcdserver: mvcc: database space exceeded") {
			cx.t.Fatal(err)
		}
	}

	// turn off alarm
	if err := ctlV3Alarm(cx, "disarm", "alarm:NOSPACE"); err != nil {
		cx.t.Fatal(err)
	}

	// put one more key below quota
	if err := ctlV3Put(cx, "ghi", smallbuf, ""); err != nil {
		cx.t.Fatal(err)
	}
}

func ctlV3Alarm(cx ctlCtx, cmd string, as ...string) error {
	cmdArgs := append(cx.PrefixArgs(), "alarm", cmd)
	return spawnWithExpects(cmdArgs, as...)
}
<|endoftext|>


<commit_before>package workspace

import (
	"os"
	"path"
	"path/filepath"
)

// Exercise is an implementation of a problem in a track.
type Exercise struct {
	Root      string
	Track     string
	Slug      string
	Documents []Document
}

// NewExerciseFromDir constructs an exercise given the exercise directory.
func NewExerciseFromDir(dir string) Exercise {
	slug := filepath.Base(dir)
	dir = filepath.Dir(dir)
	track := filepath.Base(dir)
	root := filepath.Dir(dir)
	return Exercise{Root: root, Track: track, Slug: slug}
}

// Path is the normalized relative path.
// It always has forward slashes, regardless
// of the operating system.
func (e Exercise) Path() string {
	return path.Join(e.Track, e.Slug)
}

// Filepath is the absolute path on the filesystem.
func (e Exercise) Filepath() string {
	return filepath.Join(e.Root, e.Track, e.Slug)
}

// MetadataFilepath is the absolute path to the exercise metadata.
func (e Exercise) MetadataFilepath() string {
	return filepath.Join(e.Filepath(), metadataFilepath)
}

// LegacyMetadataFilepath is the absolute path to the legacy exercise metadata.
func (e Exercise) LegacyMetadataFilepath() string {
	return filepath.Join(e.Filepath(), legacySolutionFilename)
}

// MetadataDir returns the directory that the exercise metadata lives in.
// For now this is the exercise directory.
func (e Exercise) MetadataDir() string {
	return e.Filepath()
}

// HasMetadata checks for the presence of an exercise metadata file.
// If there is no such file, this may be a legacy exercise.
// It could also be an unrelated directory.
func (e Exercise) HasMetadata() (bool, error) {
	_, err := os.Lstat(e.MetadataFilepath())
	if os.IsNotExist(err) {
		return false, nil
	}
	if err == nil {
		return true, nil
	}
	return false, err
}

// HasLegacyMetadata checks for the presence of a legacy exercise metadata file.
// If there is no such file, it could also be an unrelated directory.
func (e Exercise) HasLegacyMetadata() (bool, error) {
	_, err := os.Lstat(e.LegacyMetadataFilepath())
	if os.IsNotExist(err) {
		return false, nil
	}
	if err == nil {
		return true, nil
	}
	return false, err
}

// MigrationStatus represents the result of migrating a legacy metadata file.
type MigrationStatus int

// MigrationStatus
const (
	MigrationStatusErrorMkdir MigrationStatus = iota
	MigrationStatusErrorRename
	MigrationStatusErrorRemove
	MigrationStatusNoop
	MigrationStatusMigrated
	MigrationStatusRemoved
)

// MigrateLegacyMetadataFile migrates a legacy metadata to the modern location.
// This is a noop if the metadata file isn't legacy.
// If both legacy and modern metadata files exist, the legacy file will be deleted.
func (e Exercise) MigrateLegacyMetadataFile() (MigrationStatus, error) {
	if ok, _ := e.HasLegacyMetadata(); !ok {
		return MigrationStatusNoop, nil
	}
	legacyMetadataFilepath := e.LegacyMetadataFilepath()
	metadataFilepath := e.MetadataFilepath()
	if err := os.MkdirAll(filepath.Dir(metadataFilepath), os.FileMode(0755)); err != nil {
		return MigrationStatusErrorMkdir, err
	}
	if ok, _ := e.HasMetadata(); !ok {
		if err := os.Rename(legacyMetadataFilepath, metadataFilepath); err != nil {
			return MigrationStatusErrorRename, err
		}
		return MigrationStatusMigrated, nil
	}
	if err := os.Remove(legacyMetadataFilepath); err != nil {
		return MigrationStatusErrorRemove, err
	}
	return MigrationStatusRemoved, nil
}
<commit_msg>Fix missing word from godoc<commit_after>package workspace

import (
	"os"
	"path"
	"path/filepath"
)

// Exercise is an implementation of a problem in a track.
type Exercise struct {
	Root      string
	Track     string
	Slug      string
	Documents []Document
}

// NewExerciseFromDir constructs an exercise given the exercise directory.
func NewExerciseFromDir(dir string) Exercise {
	slug := filepath.Base(dir)
	dir = filepath.Dir(dir)
	track := filepath.Base(dir)
	root := filepath.Dir(dir)
	return Exercise{Root: root, Track: track, Slug: slug}
}

// Path is the normalized relative path.
// It always has forward slashes, regardless
// of the operating system.
func (e Exercise) Path() string {
	return path.Join(e.Track, e.Slug)
}

// Filepath is the absolute path on the filesystem.
func (e Exercise) Filepath() string {
	return filepath.Join(e.Root, e.Track, e.Slug)
}

// MetadataFilepath is the absolute path to the exercise metadata.
func (e Exercise) MetadataFilepath() string {
	return filepath.Join(e.Filepath(), metadataFilepath)
}

// LegacyMetadataFilepath is the absolute path to the legacy exercise metadata.
func (e Exercise) LegacyMetadataFilepath() string {
	return filepath.Join(e.Filepath(), legacySolutionFilename)
}

// MetadataDir returns the directory that the exercise metadata lives in.
// For now this is the exercise directory.
func (e Exercise) MetadataDir() string {
	return e.Filepath()
}

// HasMetadata checks for the presence of an exercise metadata file.
// If there is no such file, this may be a legacy exercise.
// It could also be an unrelated directory.
func (e Exercise) HasMetadata() (bool, error) {
	_, err := os.Lstat(e.MetadataFilepath())
	if os.IsNotExist(err) {
		return false, nil
	}
	if err == nil {
		return true, nil
	}
	return false, err
}

// HasLegacyMetadata checks for the presence of a legacy exercise metadata file.
// If there is no such file, it could also be an unrelated directory.
func (e Exercise) HasLegacyMetadata() (bool, error) {
	_, err := os.Lstat(e.LegacyMetadataFilepath())
	if os.IsNotExist(err) {
		return false, nil
	}
	if err == nil {
		return true, nil
	}
	return false, err
}

// MigrationStatus represents the result of migrating a legacy metadata file.
type MigrationStatus int

// MigrationStatus
const (
	MigrationStatusErrorMkdir MigrationStatus = iota
	MigrationStatusErrorRename
	MigrationStatusErrorRemove
	MigrationStatusNoop
	MigrationStatusMigrated
	MigrationStatusRemoved
)

// MigrateLegacyMetadataFile migrates a legacy metadata file to the modern location.
// This is a noop if the metadata file isn't legacy.
// If both legacy and modern metadata files exist, the legacy file will be deleted.
func (e Exercise) MigrateLegacyMetadataFile() (MigrationStatus, error) {
	if ok, _ := e.HasLegacyMetadata(); !ok {
		return MigrationStatusNoop, nil
	}
	legacyMetadataFilepath := e.LegacyMetadataFilepath()
	metadataFilepath := e.MetadataFilepath()
	if err := os.MkdirAll(filepath.Dir(metadataFilepath), os.FileMode(0755)); err != nil {
		return MigrationStatusErrorMkdir, err
	}
	if ok, _ := e.HasMetadata(); !ok {
		if err := os.Rename(legacyMetadataFilepath, metadataFilepath); err != nil {
			return MigrationStatusErrorRename, err
		}
		return MigrationStatusMigrated, nil
	}
	if err := os.Remove(legacyMetadataFilepath); err != nil {
		return MigrationStatusErrorRemove, err
	}
	return MigrationStatusRemoved, nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Cisco and/or its affiliates.
//
// 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 dbadapter

import (
	"github.com/ligato/cn-infra/db/keyval"
	"github.com/ligato/vpp-agent/clientv1/defaultplugins"
	"github.com/ligato/vpp-agent/plugins/defaultplugins/common/model/acl"
	"github.com/ligato/vpp-agent/plugins/defaultplugins/common/model/bfd"
	"github.com/ligato/vpp-agent/plugins/defaultplugins/common/model/interfaces"
	intf "github.com/ligato/vpp-agent/plugins/defaultplugins/common/model/interfaces"
	"github.com/ligato/vpp-agent/plugins/defaultplugins/common/model/ipsec"
	"github.com/ligato/vpp-agent/plugins/defaultplugins/common/model/l2"
	"github.com/ligato/vpp-agent/plugins/defaultplugins/common/model/l3"
	"github.com/ligato/vpp-agent/plugins/defaultplugins/common/model/l4"
	"github.com/ligato/vpp-agent/plugins/defaultplugins/common/model/nat"
	"github.com/ligato/vpp-agent/plugins/defaultplugins/common/model/stn"
)

// NewDataChangeDSL returns a new instance of DataChangeDSL which implements
// the data change DSL for VPP config.
// Transaction <txn> is used to propagate changes to plugins.
func NewDataChangeDSL(txn keyval.ProtoTxn) *DataChangeDSL {
	return &DataChangeDSL{txn: txn}
}

// DataChangeDSL is an implementation of Domain Specific Language (DSL)
// for changes of the VPP configuration.
type DataChangeDSL struct {
	txn keyval.ProtoTxn
}

// PutDSL implements put operations of data change DSL.
type PutDSL struct {
	parent *DataChangeDSL
}

// DeleteDSL implements delete operations of data change DSL.
type DeleteDSL struct {
	parent *DataChangeDSL
}

// Put initiates a chained sequence of data change DSL statements declaring
// new configurable objects or changing existing ones.
func (dsl *DataChangeDSL) Put() defaultplugins.PutDSL {
	return &PutDSL{dsl}
}

// Delete initiates a chained sequence of data change DSL statements
// removing existing configurable objects.
func (dsl *DataChangeDSL) Delete() defaultplugins.DeleteDSL {
	return &DeleteDSL{dsl}
}

// Send propagates requested changes to the plugins.
func (dsl *DataChangeDSL) Send() defaultplugins.Reply {
	err := dsl.txn.Commit()
	return &Reply{err}
}

// Interface adds a request to create or update VPP network interface.
func (dsl *PutDSL) Interface(val *interfaces.Interfaces_Interface) defaultplugins.PutDSL {
	dsl.parent.txn.Put(intf.InterfaceKey(val.Name), val)
	return dsl
}

// BfdSession adds a request to create or update bidirectional forwarding
// detection session.
func (dsl *PutDSL) BfdSession(val *bfd.SingleHopBFD_Session) defaultplugins.PutDSL {
	dsl.parent.txn.Put(bfd.SessionKey(val.Interface), val)
	return dsl
}

// BfdAuthKeys adds a request to create or update bidirectional forwarding
// detection key.
func (dsl *PutDSL) BfdAuthKeys(val *bfd.SingleHopBFD_Key) defaultplugins.PutDSL {
	dsl.parent.txn.Put(bfd.AuthKeysKey(string(val.Id)), val)
	return dsl
}

// BfdEchoFunction adds a request to create or update bidirectional forwarding
// detection echo function.
func (dsl *PutDSL) BfdEchoFunction(val *bfd.SingleHopBFD_EchoFunction) defaultplugins.PutDSL {
	dsl.parent.txn.Put(bfd.EchoFunctionKey(val.EchoSourceInterface), val)
	return dsl
}

// BD adds a request to create or update VPP Bridge Domain.
func (dsl *PutDSL) BD(val *l2.BridgeDomains_BridgeDomain) defaultplugins.PutDSL {
	dsl.parent.txn.Put(l2.BridgeDomainKey(val.Name), val)
	return dsl
}

// BDFIB adds a request to create or update VPP L2 Forwarding Information Base.
func (dsl *PutDSL) BDFIB(val *l2.FibTableEntries_FibTableEntry) defaultplugins.PutDSL {
	dsl.parent.txn.Put(l2.FibKey(val.BridgeDomain, val.PhysAddress), val)
	return dsl
}

// XConnect adds a request to create or update VPP Cross Connect.
func (dsl *PutDSL) XConnect(val *l2.XConnectPairs_XConnectPair) defaultplugins.PutDSL {
	dsl.parent.txn.Put(l2.XConnectKey(val.ReceiveInterface), val)
	return dsl
}

// StaticRoute adds a request to create or update VPP L3 Static Route.
func (dsl *PutDSL) StaticRoute(val *l3.StaticRoutes_Route) defaultplugins.PutDSL {
	dsl.parent.txn.Put(l3.RouteKey(val.VrfId, val.DstIpAddr, val.NextHopAddr), val)
	return dsl
}

// ACL adds a request to create or update VPP Access Control List.
func (dsl *PutDSL) ACL(val *acl.AccessLists_Acl) defaultplugins.PutDSL {
	dsl.parent.txn.Put(acl.Key(val.AclName), val)
	return dsl
}

// L4Features create or update request for the L4Features
func (dsl *PutDSL) L4Features(val *l4.L4Features) defaultplugins.PutDSL {
	dsl.parent.txn.Put(l4.FeatureKey(), val)

	return dsl
}

// AppNamespace create or update request for the Application Namespaces List
func (dsl *PutDSL) AppNamespace(val *l4.AppNamespaces_AppNamespace) defaultplugins.PutDSL {
	dsl.parent.txn.Put(l4.AppNamespacesKey(val.NamespaceId), val)

	return dsl
}

// Arp adds a request to create or update VPP L3 ARP entry.
func (dsl *PutDSL) Arp(arp *l3.ArpTable_ArpTableEntry) defaultplugins.PutDSL {
	dsl.parent.txn.Put(l3.ArpEntryKey(arp.Interface, arp.IpAddress), arp)
	return dsl
}

// StnRule adds a request to create or update STN rule.
func (dsl *PutDSL) StnRule(val *stn.StnRule) defaultplugins.PutDSL {
	dsl.parent.txn.Put(stn.Key(val.RuleName), val)
	return dsl
}

// NAT44Global adds a request to set global configuration for NAT44
func (dsl *PutDSL) NAT44Global(nat44 *nat.Nat44Global) defaultplugins.PutDSL {
	dsl.parent.txn.Put(nat.GlobalConfigKey(), nat44)
	return dsl
}

// NAT44DNat adds a request to create a new DNAT configuration
func (dsl *PutDSL) NAT44DNat(nat44 *nat.Nat44DNat_DNatConfig) defaultplugins.PutDSL {
	dsl.parent.txn.Put(nat.DNatKey(nat44.Label), nat44)
	return dsl
}

// IPSecSA adds request to create a new Security Association
func (dsl *PutDSL) IPSecSA(sa *ipsec.SecurityAssociations_SA) defaultplugins.PutDSL {
	dsl.parent.txn.Put(ipsec.SAKey(sa.Name), sa)
	return dsl
}

// IPSecSPD adds request to create a new Security Policy Database
func (dsl *PutDSL) IPSecSPD(spd *ipsec.SecurityPolicyDatabases_SPD) defaultplugins.PutDSL {
	dsl.parent.txn.Put(ipsec.SAKey(spd.Name), spd)
	return dsl
}

// Delete changes the DSL mode to allow removal of an existing configuration.
func (dsl *PutDSL) Delete() defaultplugins.DeleteDSL {
	return &DeleteDSL{dsl.parent}
}

// Send propagates requested changes to the plugins.
func (dsl *PutDSL) Send() defaultplugins.Reply {
	return dsl.parent.Send()
}

// Interface adds a request to delete an existing VPP network interface.
func (dsl *DeleteDSL) Interface(interfaceName string) defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(intf.InterfaceKey(interfaceName))
	return dsl
}

// BfdSession adds a request to delete an existing bidirectional forwarding
// detection session.
func (dsl *DeleteDSL) BfdSession(bfdSessionIfaceName string) defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(bfd.SessionKey(bfdSessionIfaceName))
	return dsl
}

// BfdAuthKeys adds a request to delete an existing bidirectional forwarding
// detection key.
func (dsl *DeleteDSL) BfdAuthKeys(bfdKey string) defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(bfd.AuthKeysKey(bfdKey))
	return dsl
}

// BfdEchoFunction adds a request to delete an existing bidirectional forwarding
// detection echo function.
func (dsl *DeleteDSL) BfdEchoFunction(bfdEchoName string) defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(bfd.EchoFunctionKey(bfdEchoName))
	return dsl
}

// BD adds a request to delete an existing VPP Bridge Domain.
func (dsl *DeleteDSL) BD(bdName string) defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(l2.BridgeDomainKey(bdName))
	return dsl
}

// BDFIB adds a request to delete an existing VPP L2 Forwarding Information
// Base.
func (dsl *DeleteDSL) BDFIB(bdName string, mac string) defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(l2.FibKey(bdName, mac))
	return dsl
}

// XConnect adds a request to delete an existing VPP Cross Connect.
func (dsl *DeleteDSL) XConnect(rxIfName string) defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(l2.XConnectKey(rxIfName))
	return dsl
}

// StaticRoute adds a request to delete an existing VPP L3 Static Route.
func (dsl *DeleteDSL) StaticRoute(vrf uint32, dstAddr string, nextHopAddr string) defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(l3.RouteKey(vrf, dstAddr, nextHopAddr))
	return dsl
}

// ACL adds a request to delete an existing VPP Access Control List.
func (dsl *DeleteDSL) ACL(aclName string) defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(acl.Key(aclName))
	return dsl
}

// L4Features delete request for the L4Features
func (dsl *DeleteDSL) L4Features() defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(l4.FeatureKey())
	return dsl
}

// Arp adds a request to delete an existing VPP L3 ARP entry.
func (dsl *DeleteDSL) Arp(ifaceName string, ipAddr string) defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(l3.ArpEntryKey(ifaceName, ipAddr))
	return dsl
}

// AppNamespace adds a request to delete an existing VPP Application Namespace.
func (dsl *DeleteDSL) AppNamespace(id string) defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(l4.AppNamespacesKey(id))
	return dsl
}

// StnRule adds request to delete Stn rule.
func (dsl *DeleteDSL) StnRule(ruleName string) defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(stn.Key(ruleName))
	return dsl
}

// NAT44Global adds a request to remove global configuration for NAT44
func (dsl *DeleteDSL) NAT44Global() defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(nat.GlobalConfigKey())
	return dsl
}

// NAT44DNat adds a request to delete a new DNAT configuration
func (dsl *DeleteDSL) NAT44DNat(label string) defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(nat.DNatKey(label))
	return dsl
}

// IPSecSA adds request to create a new Security Association
func (dsl *DeleteDSL) IPSecSA(saName string) defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(ipsec.SAKey(saName))
	return dsl
}

// IPSecSPD adds request to create a new Security Policy Database
func (dsl *DeleteDSL) IPSecSPD(spdName string) defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(ipsec.SPDKey(spdName))
	return dsl
}

// Put changes the DSL mode to allow configuration editing.
func (dsl *DeleteDSL) Put() defaultplugins.PutDSL {
	return &PutDSL{dsl.parent}
}

// Send propagates requested changes to the plugins.
func (dsl *DeleteDSL) Send() defaultplugins.Reply {
	return dsl.parent.Send()
}

// Reply interface allows to wait for a reply to previously called Send() and
// extract the result from it (success/error).
type Reply struct {
	err error
}

// ReceiveReply waits for a reply to previously called Send() and returns
// the result (error or nil).
func (dsl Reply) ReceiveReply() error {
	return dsl.err
}
<commit_msg>Fix client API key for SPD<commit_after>// Copyright (c) 2017 Cisco and/or its affiliates.
//
// 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 dbadapter

import (
	"github.com/ligato/cn-infra/db/keyval"
	"github.com/ligato/vpp-agent/clientv1/defaultplugins"
	"github.com/ligato/vpp-agent/plugins/defaultplugins/common/model/acl"
	"github.com/ligato/vpp-agent/plugins/defaultplugins/common/model/bfd"
	"github.com/ligato/vpp-agent/plugins/defaultplugins/common/model/interfaces"
	intf "github.com/ligato/vpp-agent/plugins/defaultplugins/common/model/interfaces"
	"github.com/ligato/vpp-agent/plugins/defaultplugins/common/model/ipsec"
	"github.com/ligato/vpp-agent/plugins/defaultplugins/common/model/l2"
	"github.com/ligato/vpp-agent/plugins/defaultplugins/common/model/l3"
	"github.com/ligato/vpp-agent/plugins/defaultplugins/common/model/l4"
	"github.com/ligato/vpp-agent/plugins/defaultplugins/common/model/nat"
	"github.com/ligato/vpp-agent/plugins/defaultplugins/common/model/stn"
)

// NewDataChangeDSL returns a new instance of DataChangeDSL which implements
// the data change DSL for VPP config.
// Transaction <txn> is used to propagate changes to plugins.
func NewDataChangeDSL(txn keyval.ProtoTxn) *DataChangeDSL {
	return &DataChangeDSL{txn: txn}
}

// DataChangeDSL is an implementation of Domain Specific Language (DSL)
// for changes of the VPP configuration.
type DataChangeDSL struct {
	txn keyval.ProtoTxn
}

// PutDSL implements put operations of data change DSL.
type PutDSL struct {
	parent *DataChangeDSL
}

// DeleteDSL implements delete operations of data change DSL.
type DeleteDSL struct {
	parent *DataChangeDSL
}

// Put initiates a chained sequence of data change DSL statements declaring
// new configurable objects or changing existing ones.
func (dsl *DataChangeDSL) Put() defaultplugins.PutDSL {
	return &PutDSL{dsl}
}

// Delete initiates a chained sequence of data change DSL statements
// removing existing configurable objects.
func (dsl *DataChangeDSL) Delete() defaultplugins.DeleteDSL {
	return &DeleteDSL{dsl}
}

// Send propagates requested changes to the plugins.
func (dsl *DataChangeDSL) Send() defaultplugins.Reply {
	err := dsl.txn.Commit()
	return &Reply{err}
}

// Interface adds a request to create or update VPP network interface.
func (dsl *PutDSL) Interface(val *interfaces.Interfaces_Interface) defaultplugins.PutDSL {
	dsl.parent.txn.Put(intf.InterfaceKey(val.Name), val)
	return dsl
}

// BfdSession adds a request to create or update bidirectional forwarding
// detection session.
func (dsl *PutDSL) BfdSession(val *bfd.SingleHopBFD_Session) defaultplugins.PutDSL {
	dsl.parent.txn.Put(bfd.SessionKey(val.Interface), val)
	return dsl
}

// BfdAuthKeys adds a request to create or update bidirectional forwarding
// detection key.
func (dsl *PutDSL) BfdAuthKeys(val *bfd.SingleHopBFD_Key) defaultplugins.PutDSL {
	dsl.parent.txn.Put(bfd.AuthKeysKey(string(val.Id)), val)
	return dsl
}

// BfdEchoFunction adds a request to create or update bidirectional forwarding
// detection echo function.
func (dsl *PutDSL) BfdEchoFunction(val *bfd.SingleHopBFD_EchoFunction) defaultplugins.PutDSL {
	dsl.parent.txn.Put(bfd.EchoFunctionKey(val.EchoSourceInterface), val)
	return dsl
}

// BD adds a request to create or update VPP Bridge Domain.
func (dsl *PutDSL) BD(val *l2.BridgeDomains_BridgeDomain) defaultplugins.PutDSL {
	dsl.parent.txn.Put(l2.BridgeDomainKey(val.Name), val)
	return dsl
}

// BDFIB adds a request to create or update VPP L2 Forwarding Information Base.
func (dsl *PutDSL) BDFIB(val *l2.FibTableEntries_FibTableEntry) defaultplugins.PutDSL {
	dsl.parent.txn.Put(l2.FibKey(val.BridgeDomain, val.PhysAddress), val)
	return dsl
}

// XConnect adds a request to create or update VPP Cross Connect.
func (dsl *PutDSL) XConnect(val *l2.XConnectPairs_XConnectPair) defaultplugins.PutDSL {
	dsl.parent.txn.Put(l2.XConnectKey(val.ReceiveInterface), val)
	return dsl
}

// StaticRoute adds a request to create or update VPP L3 Static Route.
func (dsl *PutDSL) StaticRoute(val *l3.StaticRoutes_Route) defaultplugins.PutDSL {
	dsl.parent.txn.Put(l3.RouteKey(val.VrfId, val.DstIpAddr, val.NextHopAddr), val)
	return dsl
}

// ACL adds a request to create or update VPP Access Control List.
func (dsl *PutDSL) ACL(val *acl.AccessLists_Acl) defaultplugins.PutDSL {
	dsl.parent.txn.Put(acl.Key(val.AclName), val)
	return dsl
}

// L4Features create or update request for the L4Features
func (dsl *PutDSL) L4Features(val *l4.L4Features) defaultplugins.PutDSL {
	dsl.parent.txn.Put(l4.FeatureKey(), val)

	return dsl
}

// AppNamespace create or update request for the Application Namespaces List
func (dsl *PutDSL) AppNamespace(val *l4.AppNamespaces_AppNamespace) defaultplugins.PutDSL {
	dsl.parent.txn.Put(l4.AppNamespacesKey(val.NamespaceId), val)

	return dsl
}

// Arp adds a request to create or update VPP L3 ARP entry.
func (dsl *PutDSL) Arp(arp *l3.ArpTable_ArpTableEntry) defaultplugins.PutDSL {
	dsl.parent.txn.Put(l3.ArpEntryKey(arp.Interface, arp.IpAddress), arp)
	return dsl
}

// StnRule adds a request to create or update STN rule.
func (dsl *PutDSL) StnRule(val *stn.StnRule) defaultplugins.PutDSL {
	dsl.parent.txn.Put(stn.Key(val.RuleName), val)
	return dsl
}

// NAT44Global adds a request to set global configuration for NAT44
func (dsl *PutDSL) NAT44Global(nat44 *nat.Nat44Global) defaultplugins.PutDSL {
	dsl.parent.txn.Put(nat.GlobalConfigKey(), nat44)
	return dsl
}

// NAT44DNat adds a request to create a new DNAT configuration
func (dsl *PutDSL) NAT44DNat(nat44 *nat.Nat44DNat_DNatConfig) defaultplugins.PutDSL {
	dsl.parent.txn.Put(nat.DNatKey(nat44.Label), nat44)
	return dsl
}

// IPSecSA adds request to create a new Security Association
func (dsl *PutDSL) IPSecSA(sa *ipsec.SecurityAssociations_SA) defaultplugins.PutDSL {
	dsl.parent.txn.Put(ipsec.SAKey(sa.Name), sa)
	return dsl
}

// IPSecSPD adds request to create a new Security Policy Database
func (dsl *PutDSL) IPSecSPD(spd *ipsec.SecurityPolicyDatabases_SPD) defaultplugins.PutDSL {
	dsl.parent.txn.Put(ipsec.SPDKey(spd.Name), spd)
	return dsl
}

// Delete changes the DSL mode to allow removal of an existing configuration.
func (dsl *PutDSL) Delete() defaultplugins.DeleteDSL {
	return &DeleteDSL{dsl.parent}
}

// Send propagates requested changes to the plugins.
func (dsl *PutDSL) Send() defaultplugins.Reply {
	return dsl.parent.Send()
}

// Interface adds a request to delete an existing VPP network interface.
func (dsl *DeleteDSL) Interface(interfaceName string) defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(intf.InterfaceKey(interfaceName))
	return dsl
}

// BfdSession adds a request to delete an existing bidirectional forwarding
// detection session.
func (dsl *DeleteDSL) BfdSession(bfdSessionIfaceName string) defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(bfd.SessionKey(bfdSessionIfaceName))
	return dsl
}

// BfdAuthKeys adds a request to delete an existing bidirectional forwarding
// detection key.
func (dsl *DeleteDSL) BfdAuthKeys(bfdKey string) defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(bfd.AuthKeysKey(bfdKey))
	return dsl
}

// BfdEchoFunction adds a request to delete an existing bidirectional forwarding
// detection echo function.
func (dsl *DeleteDSL) BfdEchoFunction(bfdEchoName string) defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(bfd.EchoFunctionKey(bfdEchoName))
	return dsl
}

// BD adds a request to delete an existing VPP Bridge Domain.
func (dsl *DeleteDSL) BD(bdName string) defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(l2.BridgeDomainKey(bdName))
	return dsl
}

// BDFIB adds a request to delete an existing VPP L2 Forwarding Information
// Base.
func (dsl *DeleteDSL) BDFIB(bdName string, mac string) defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(l2.FibKey(bdName, mac))
	return dsl
}

// XConnect adds a request to delete an existing VPP Cross Connect.
func (dsl *DeleteDSL) XConnect(rxIfName string) defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(l2.XConnectKey(rxIfName))
	return dsl
}

// StaticRoute adds a request to delete an existing VPP L3 Static Route.
func (dsl *DeleteDSL) StaticRoute(vrf uint32, dstAddr string, nextHopAddr string) defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(l3.RouteKey(vrf, dstAddr, nextHopAddr))
	return dsl
}

// ACL adds a request to delete an existing VPP Access Control List.
func (dsl *DeleteDSL) ACL(aclName string) defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(acl.Key(aclName))
	return dsl
}

// L4Features delete request for the L4Features
func (dsl *DeleteDSL) L4Features() defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(l4.FeatureKey())
	return dsl
}

// Arp adds a request to delete an existing VPP L3 ARP entry.
func (dsl *DeleteDSL) Arp(ifaceName string, ipAddr string) defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(l3.ArpEntryKey(ifaceName, ipAddr))
	return dsl
}

// AppNamespace adds a request to delete an existing VPP Application Namespace.
func (dsl *DeleteDSL) AppNamespace(id string) defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(l4.AppNamespacesKey(id))
	return dsl
}

// StnRule adds request to delete Stn rule.
func (dsl *DeleteDSL) StnRule(ruleName string) defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(stn.Key(ruleName))
	return dsl
}

// NAT44Global adds a request to remove global configuration for NAT44
func (dsl *DeleteDSL) NAT44Global() defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(nat.GlobalConfigKey())
	return dsl
}

// NAT44DNat adds a request to delete a new DNAT configuration
func (dsl *DeleteDSL) NAT44DNat(label string) defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(nat.DNatKey(label))
	return dsl
}

// IPSecSA adds request to create a new Security Association
func (dsl *DeleteDSL) IPSecSA(saName string) defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(ipsec.SAKey(saName))
	return dsl
}

// IPSecSPD adds request to create a new Security Policy Database
func (dsl *DeleteDSL) IPSecSPD(spdName string) defaultplugins.DeleteDSL {
	dsl.parent.txn.Delete(ipsec.SPDKey(spdName))
	return dsl
}

// Put changes the DSL mode to allow configuration editing.
func (dsl *DeleteDSL) Put() defaultplugins.PutDSL {
	return &PutDSL{dsl.parent}
}

// Send propagates requested changes to the plugins.
func (dsl *DeleteDSL) Send() defaultplugins.Reply {
	return dsl.parent.Send()
}

// Reply interface allows to wait for a reply to previously called Send() and
// extract the result from it (success/error).
type Reply struct {
	err error
}

// ReceiveReply waits for a reply to previously called Send() and returns
// the result (error or nil).
func (dsl Reply) ReceiveReply() error {
	return dsl.err
}
<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements.  See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You 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 coder contains coder representation and utilities. Coders describe
// how to serialize and deserialize pipeline data and may be provided by users.
package coder

import (
	"fmt"
	"io"
	"reflect"
	"strings"

	"github.com/apache/beam/sdks/go/pkg/beam/core/funcx"
	"github.com/apache/beam/sdks/go/pkg/beam/core/typex"
	"github.com/apache/beam/sdks/go/pkg/beam/core/util/reflectx"
)

// CustomCoder contains possibly untyped encode/decode user functions that are
// type-bound at runtime. Universal coders can thus be used for many different
// types, but each CustomCoder instance will be bound to a specific type.
type CustomCoder struct {
	// Name is the coder name. Informational only.
	Name string
	// Type is the underlying concrete type that is being coded. It is
	// available to Enc and Dec. It must be a concrete type.
	Type reflect.Type

	// Enc is the encoding function : T -> []byte. It may optionally take a
	// reflect.Type parameter and return an error as well.
	Enc *funcx.Fn
	// Dec is the decoding function: []byte -> T. It may optionally take a
	// reflect.Type parameter and return an error as well.
	Dec *funcx.Fn
}

// TODO(herohde) 5/16/2017: do we want/need to allow user coders that follow the
// internal signature, which takes io.Reader/io.Writer? Do we need size estimation?
// Maybe we can get away with just handling protos as an internal coder.

// TODO(herohde) 5/16/2017: we're ignoring the inner/outer context concept
// present in java/python. Not clear whether we actually need it.

// Equals returns true iff the two custom coders are equal. It assumes that
// functions with the same name and types are identical.
func (c *CustomCoder) Equals(o *CustomCoder) bool {
	if c == nil && o == nil {
		return true
	}
	if c == nil && o != nil || c != nil && o == nil {
		return false
	}
	if c.Name != o.Name {
		return false
	}
	if c.Type != o.Type {
		return false
	}
	if c.Dec.Fn.Name() != o.Dec.Fn.Name() {
		return false
	}
	return c.Enc.Fn.Name() == o.Enc.Fn.Name()
}

func (c *CustomCoder) String() string {
	return fmt.Sprintf("%v[%v]", c.Type, c.Name)
}

// Type signatures of encode/decode for verification.
var (
	encodeSig = &funcx.Signature{
		OptArgs:   []reflect.Type{reflectx.Type},
		Args:      []reflect.Type{typex.TType}, // T to be substituted
		Return:    []reflect.Type{reflectx.ByteSlice},
		OptReturn: []reflect.Type{reflectx.Error}}

	decodeSig = &funcx.Signature{
		OptArgs:   []reflect.Type{reflectx.Type},
		Args:      []reflect.Type{reflectx.ByteSlice},
		Return:    []reflect.Type{typex.TType}, // T to be substituted
		OptReturn: []reflect.Type{reflectx.Error}}
)

// ElementEncoder encapsulates being able to encode an element into a writer.
type ElementEncoder interface {
	Encode(element interface{}, w io.Writer) error
}

// ElementDecoder encapsulates being able to decode an element from a reader.
type ElementDecoder interface {
	Decode(r io.Reader) (interface{}, error)
}

func validateEncoder(t reflect.Type, encode interface{}) error {
	// Check if it uses the real type in question.
	if err := funcx.Satisfy(encode, funcx.Replace(encodeSig, typex.TType, t)); err != nil {
		return fmt.Errorf("validateEncoder: incorrect signature: %v", err)
	}
	// TODO(lostluck): 2019.02.03 - Determine if there are encode allocation bottlenecks.
	return nil
}

func validateDecoder(t reflect.Type, decode interface{}) error {
	// Check if it uses the real type in question.
	if err := funcx.Satisfy(decode, funcx.Replace(decodeSig, typex.TType, t)); err != nil {
		return fmt.Errorf("validateDecoder: incorrect signature: %v", err)
	}
	// TODO(lostluck): 2019.02.03 - Expand cases to avoid []byte -> interface{} conversion
	// in exec, & a beam Decoder interface.
	return nil
}

// NewCustomCoder creates a coder for the supplied parameters defining a
// particular encoding strategy.
func NewCustomCoder(id string, t reflect.Type, encode, decode interface{}) (*CustomCoder, error) {
	if err := validateEncoder(t, encode); err != nil {
		return nil, fmt.Errorf("NewCustomCoder: %v", err)
	}
	enc, err := funcx.New(reflectx.MakeFunc(encode))
	if err != nil {
		return nil, fmt.Errorf("bad encode: %v", err)
	}
	if err := validateDecoder(t, decode); err != nil {
		return nil, fmt.Errorf("NewCustomCoder: %v", err)
	}

	dec, err := funcx.New(reflectx.MakeFunc(decode))
	if err != nil {
		return nil, fmt.Errorf("bad decode: %v", err)
	}

	c := &CustomCoder{
		Name: id,
		Type: t,
		Enc:  enc,
		Dec:  dec,
	}
	return c, nil
}

// Kind represents the type of coder used.
type Kind string

// Tags for the various Beam encoding strategies. https://beam.apache.org/documentation/programming-guide/#coders
// documents the usage of coders in the Beam environment.
const (
	Custom        Kind = "Custom" // Implicitly length-prefixed
	Bytes         Kind = "bytes"  // Implicitly length-prefixed as part of the encoding
	VarInt        Kind = "varint"
	WindowedValue Kind = "W"
	KV            Kind = "KV"

	// CoGBK is currently equivalent to either
	//
	//     KV<X,Iterable<Y>>         (if GBK)
	//     KV<X,Iterable<KV<int,Y>>> (if CoGBK, using a tagged union encoding)
	//
	// It requires special handling in translation to the model pipeline in the latter case
	// to add the incoming index for each input.
	//
	// TODO(BEAM-490): once this JIRA is done, this coder should become the new thing.
	CoGBK Kind = "CoGBK"
)

// Coder is a description of how to encode and decode values of a given type.
// Except for the "custom" kind, they are built in and must adhere to the
// (unwritten) Beam specification.
type Coder struct {
	Kind Kind
	T    typex.FullType

	Components []*Coder     // WindowedValue, KV, CoGBK
	Custom     *CustomCoder // Custom
	Window     *WindowCoder // WindowedValue
}

// Equals returns true iff the two coders are equal. It assumes that
// functions with the same name and types are identical.
func (c *Coder) Equals(o *Coder) bool {
	if c.Kind != o.Kind {
		return false
	}
	if !typex.IsEqual(c.T, o.T) {
		return false
	}
	if len(c.Components) != len(o.Components) {
		return false
	}
	for i, elm := range c.Components {
		if !elm.Equals(o.Components[i]) {
			return false
		}
	}
	if c.Custom != nil {
		if !c.Custom.Equals(o.Custom) {
			return false
		}
	}
	if c.Window != nil {
		if !c.Window.Equals(o.Window) {
			return false
		}
	}
	return true
}

func (c *Coder) String() string {
	if c == nil {
		return "$"
	}
	if c.Custom != nil {
		return c.Custom.String()
	}

	ret := fmt.Sprintf("%v", c.Kind)
	if len(c.Components) > 0 {
		var args []string
		for _, elm := range c.Components {
			args = append(args, fmt.Sprintf("%v", elm))
		}
		ret += fmt.Sprintf("<%v>", strings.Join(args, ","))
	}
	if c.Window != nil {
		ret += fmt.Sprintf("!%v", c.Window)
	}
	return ret
}

// NewBytes returns a new []byte coder using the built-in scheme. It
// is always nested, for now.
func NewBytes() *Coder {
	return &Coder{Kind: Bytes, T: typex.New(reflectx.ByteSlice)}
}

// NewVarInt returns a new int32 coder using the built-in scheme.
func NewVarInt() *Coder {
	return &Coder{Kind: VarInt, T: typex.New(reflectx.Int64)}
}

// IsW returns true iff the coder is for a WindowedValue.
func IsW(c *Coder) bool {
	return c.Kind == WindowedValue
}

// NewW returns a WindowedValue coder for the window of elements.
func NewW(c *Coder, w *WindowCoder) *Coder {
	if c == nil {
		panic("coder must not be nil")
	}
	if w == nil {
		panic("window must not be nil")
	}

	return &Coder{
		Kind:       WindowedValue,
		T:          typex.NewW(c.T),
		Window:     w,
		Components: []*Coder{c},
	}
}

// IsKV returns true iff the coder is for key-value pairs.
func IsKV(c *Coder) bool {
	return c.Kind == KV
}

// NewKV returns a coder for key-value pairs.
func NewKV(components []*Coder) *Coder {
	checkCodersNotNil(components)
	return &Coder{
		Kind:       KV,
		T:          typex.New(typex.KVType, Types(components)...),
		Components: components,
	}
}

// IsCoGBK returns true iff the coder is for a CoGBK type.
func IsCoGBK(c *Coder) bool {
	return c.Kind == CoGBK
}

// NewCoGBK returns a coder for CoGBK elements.
func NewCoGBK(components []*Coder) *Coder {
	checkCodersNotNil(components)
	return &Coder{
		Kind:       CoGBK,
		T:          typex.New(typex.CoGBKType, Types(components)...),
		Components: components,
	}
}

// SkipW returns the data coder used by a WindowedValue, or returns the coder. This
// allows code to seamlessly traverse WindowedValues without additional conditional
// code.
func SkipW(c *Coder) *Coder {
	if c.Kind == WindowedValue {
		return c.Components[0]
	}
	return c
}

// Types returns a slice of types used by the supplied coders.
func Types(list []*Coder) []typex.FullType {
	var ret []typex.FullType
	for _, c := range list {
		ret = append(ret, c.T)
	}
	return ret
}

func checkCodersNotNil(list []*Coder) {
	for i, c := range list {
		if c == nil {
			panic(fmt.Sprintf("nil coder at index: %v", i))
		}
	}
}
<commit_msg>Fix documentation on graph/coder.NewVarInt<commit_after>// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements.  See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You 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 coder contains coder representation and utilities. Coders describe
// how to serialize and deserialize pipeline data and may be provided by users.
package coder

import (
	"fmt"
	"io"
	"reflect"
	"strings"

	"github.com/apache/beam/sdks/go/pkg/beam/core/funcx"
	"github.com/apache/beam/sdks/go/pkg/beam/core/typex"
	"github.com/apache/beam/sdks/go/pkg/beam/core/util/reflectx"
)

// CustomCoder contains possibly untyped encode/decode user functions that are
// type-bound at runtime. Universal coders can thus be used for many different
// types, but each CustomCoder instance will be bound to a specific type.
type CustomCoder struct {
	// Name is the coder name. Informational only.
	Name string
	// Type is the underlying concrete type that is being coded. It is
	// available to Enc and Dec. It must be a concrete type.
	Type reflect.Type

	// Enc is the encoding function : T -> []byte. It may optionally take a
	// reflect.Type parameter and return an error as well.
	Enc *funcx.Fn
	// Dec is the decoding function: []byte -> T. It may optionally take a
	// reflect.Type parameter and return an error as well.
	Dec *funcx.Fn
}

// TODO(herohde) 5/16/2017: do we want/need to allow user coders that follow the
// internal signature, which takes io.Reader/io.Writer? Do we need size estimation?
// Maybe we can get away with just handling protos as an internal coder.

// TODO(herohde) 5/16/2017: we're ignoring the inner/outer context concept
// present in java/python. Not clear whether we actually need it.

// Equals returns true iff the two custom coders are equal. It assumes that
// functions with the same name and types are identical.
func (c *CustomCoder) Equals(o *CustomCoder) bool {
	if c == nil && o == nil {
		return true
	}
	if c == nil && o != nil || c != nil && o == nil {
		return false
	}
	if c.Name != o.Name {
		return false
	}
	if c.Type != o.Type {
		return false
	}
	if c.Dec.Fn.Name() != o.Dec.Fn.Name() {
		return false
	}
	return c.Enc.Fn.Name() == o.Enc.Fn.Name()
}

func (c *CustomCoder) String() string {
	return fmt.Sprintf("%v[%v]", c.Type, c.Name)
}

// Type signatures of encode/decode for verification.
var (
	encodeSig = &funcx.Signature{
		OptArgs:   []reflect.Type{reflectx.Type},
		Args:      []reflect.Type{typex.TType}, // T to be substituted
		Return:    []reflect.Type{reflectx.ByteSlice},
		OptReturn: []reflect.Type{reflectx.Error}}

	decodeSig = &funcx.Signature{
		OptArgs:   []reflect.Type{reflectx.Type},
		Args:      []reflect.Type{reflectx.ByteSlice},
		Return:    []reflect.Type{typex.TType}, // T to be substituted
		OptReturn: []reflect.Type{reflectx.Error}}
)

// ElementEncoder encapsulates being able to encode an element into a writer.
type ElementEncoder interface {
	Encode(element interface{}, w io.Writer) error
}

// ElementDecoder encapsulates being able to decode an element from a reader.
type ElementDecoder interface {
	Decode(r io.Reader) (interface{}, error)
}

func validateEncoder(t reflect.Type, encode interface{}) error {
	// Check if it uses the real type in question.
	if err := funcx.Satisfy(encode, funcx.Replace(encodeSig, typex.TType, t)); err != nil {
		return fmt.Errorf("validateEncoder: incorrect signature: %v", err)
	}
	// TODO(lostluck): 2019.02.03 - Determine if there are encode allocation bottlenecks.
	return nil
}

func validateDecoder(t reflect.Type, decode interface{}) error {
	// Check if it uses the real type in question.
	if err := funcx.Satisfy(decode, funcx.Replace(decodeSig, typex.TType, t)); err != nil {
		return fmt.Errorf("validateDecoder: incorrect signature: %v", err)
	}
	// TODO(lostluck): 2019.02.03 - Expand cases to avoid []byte -> interface{} conversion
	// in exec, & a beam Decoder interface.
	return nil
}

// NewCustomCoder creates a coder for the supplied parameters defining a
// particular encoding strategy.
func NewCustomCoder(id string, t reflect.Type, encode, decode interface{}) (*CustomCoder, error) {
	if err := validateEncoder(t, encode); err != nil {
		return nil, fmt.Errorf("NewCustomCoder: %v", err)
	}
	enc, err := funcx.New(reflectx.MakeFunc(encode))
	if err != nil {
		return nil, fmt.Errorf("bad encode: %v", err)
	}
	if err := validateDecoder(t, decode); err != nil {
		return nil, fmt.Errorf("NewCustomCoder: %v", err)
	}

	dec, err := funcx.New(reflectx.MakeFunc(decode))
	if err != nil {
		return nil, fmt.Errorf("bad decode: %v", err)
	}

	c := &CustomCoder{
		Name: id,
		Type: t,
		Enc:  enc,
		Dec:  dec,
	}
	return c, nil
}

// Kind represents the type of coder used.
type Kind string

// Tags for the various Beam encoding strategies. https://beam.apache.org/documentation/programming-guide/#coders
// documents the usage of coders in the Beam environment.
const (
	Custom        Kind = "Custom" // Implicitly length-prefixed
	Bytes         Kind = "bytes"  // Implicitly length-prefixed as part of the encoding
	VarInt        Kind = "varint"
	WindowedValue Kind = "W"
	KV            Kind = "KV"

	// CoGBK is currently equivalent to either
	//
	//     KV<X,Iterable<Y>>         (if GBK)
	//     KV<X,Iterable<KV<int,Y>>> (if CoGBK, using a tagged union encoding)
	//
	// It requires special handling in translation to the model pipeline in the latter case
	// to add the incoming index for each input.
	//
	// TODO(BEAM-490): once this JIRA is done, this coder should become the new thing.
	CoGBK Kind = "CoGBK"
)

// Coder is a description of how to encode and decode values of a given type.
// Except for the "custom" kind, they are built in and must adhere to the
// (unwritten) Beam specification.
type Coder struct {
	Kind Kind
	T    typex.FullType

	Components []*Coder     // WindowedValue, KV, CoGBK
	Custom     *CustomCoder // Custom
	Window     *WindowCoder // WindowedValue
}

// Equals returns true iff the two coders are equal. It assumes that
// functions with the same name and types are identical.
func (c *Coder) Equals(o *Coder) bool {
	if c.Kind != o.Kind {
		return false
	}
	if !typex.IsEqual(c.T, o.T) {
		return false
	}
	if len(c.Components) != len(o.Components) {
		return false
	}
	for i, elm := range c.Components {
		if !elm.Equals(o.Components[i]) {
			return false
		}
	}
	if c.Custom != nil {
		if !c.Custom.Equals(o.Custom) {
			return false
		}
	}
	if c.Window != nil {
		if !c.Window.Equals(o.Window) {
			return false
		}
	}
	return true
}

func (c *Coder) String() string {
	if c == nil {
		return "$"
	}
	if c.Custom != nil {
		return c.Custom.String()
	}

	ret := fmt.Sprintf("%v", c.Kind)
	if len(c.Components) > 0 {
		var args []string
		for _, elm := range c.Components {
			args = append(args, fmt.Sprintf("%v", elm))
		}
		ret += fmt.Sprintf("<%v>", strings.Join(args, ","))
	}
	if c.Window != nil {
		ret += fmt.Sprintf("!%v", c.Window)
	}
	return ret
}

// NewBytes returns a new []byte coder using the built-in scheme. It
// is always nested, for now.
func NewBytes() *Coder {
	return &Coder{Kind: Bytes, T: typex.New(reflectx.ByteSlice)}
}

// NewVarInt returns a new int64 coder using the built-in scheme.
func NewVarInt() *Coder {
	return &Coder{Kind: VarInt, T: typex.New(reflectx.Int64)}
}

// IsW returns true iff the coder is for a WindowedValue.
func IsW(c *Coder) bool {
	return c.Kind == WindowedValue
}

// NewW returns a WindowedValue coder for the window of elements.
func NewW(c *Coder, w *WindowCoder) *Coder {
	if c == nil {
		panic("coder must not be nil")
	}
	if w == nil {
		panic("window must not be nil")
	}

	return &Coder{
		Kind:       WindowedValue,
		T:          typex.NewW(c.T),
		Window:     w,
		Components: []*Coder{c},
	}
}

// IsKV returns true iff the coder is for key-value pairs.
func IsKV(c *Coder) bool {
	return c.Kind == KV
}

// NewKV returns a coder for key-value pairs.
func NewKV(components []*Coder) *Coder {
	checkCodersNotNil(components)
	return &Coder{
		Kind:       KV,
		T:          typex.New(typex.KVType, Types(components)...),
		Components: components,
	}
}

// IsCoGBK returns true iff the coder is for a CoGBK type.
func IsCoGBK(c *Coder) bool {
	return c.Kind == CoGBK
}

// NewCoGBK returns a coder for CoGBK elements.
func NewCoGBK(components []*Coder) *Coder {
	checkCodersNotNil(components)
	return &Coder{
		Kind:       CoGBK,
		T:          typex.New(typex.CoGBKType, Types(components)...),
		Components: components,
	}
}

// SkipW returns the data coder used by a WindowedValue, or returns the coder. This
// allows code to seamlessly traverse WindowedValues without additional conditional
// code.
func SkipW(c *Coder) *Coder {
	if c.Kind == WindowedValue {
		return c.Components[0]
	}
	return c
}

// Types returns a slice of types used by the supplied coders.
func Types(list []*Coder) []typex.FullType {
	var ret []typex.FullType
	for _, c := range list {
		ret = append(ret, c.T)
	}
	return ret
}

func checkCodersNotNil(list []*Coder) {
	for i, c := range list {
		if c == nil {
			panic(fmt.Sprintf("nil coder at index: %v", i))
		}
	}
}
<|endoftext|>