<commit_before>// Package osmpbf decodes OpenStreetMap pbf files.
// Use this package by creating a NewDecoder and passing it a PBF file. Use
// Decode to return Node, Way and Relation structs.
package osmpbf

import (
	"bytes"
	"code.google.com/p/goprotobuf/proto"
	"compress/zlib"
	"encoding/binary"
	"errors"
	"fmt"
	"github.com/qedus/osmpbf/OSMPBF"
	"io"
	"time"
)

const (
	maxBlobHeaderSize = 64 * 1024
	maxBlobSize       = 32 * 1024 * 1024
)

var (
	parseCapabilities = map[string]bool{
		"OsmSchema-V0.6": true,
		"DenseNodes":     true,
	}
)

type Node struct {
	ID        int64
	Lat       float64
	Lon       float64
	Tags      map[string]string
	Timestamp time.Time

	// TODO: Add more DenseInfo fields
}

type Way struct {
	ID        int64
	Tags      map[string]string
	NodeIDs   []int64
	Timestamp time.Time

	// TODO: Add more Info fields
}

type Relation struct {
	ID        int64
	Tags      map[string]string
	Members   []Member
	Timestamp time.Time

	// TODO: Add more Info fields
	// TODO: Add roles_sid
}

type MemberType int

const (
	NodeType MemberType = iota
	WayType
	RelationType
)

type Member struct {
	ID   int64
	Type MemberType
	Role string
}

type pair struct {
	i interface{}
	e error
}

// A Decoder reads and decodes OpenStreetMap PBF data from an input stream.
type Decoder struct {
	r          io.Reader
	serializer chan pair

	// for data decoders
	inputs  []chan<- pair
	outputs []<-chan pair
}

// NewDecoder returns a new decoder that reads from r.
func NewDecoder(r io.Reader) *Decoder {
	return &Decoder{
		r:          r,
		serializer: make(chan pair, 8000), // typical PrimitiveBlock contains 8k OSM entities
	}
}

// Start decoding process using n goroutines.
func (dec *Decoder) Start(n int) error {
	if n < 1 {
		n = 1
	}

	// read OSMHeader
	blobHeader, blob, err := dec.readFileBlock()
	if err == nil {
		if blobHeader.GetType() == "OSMHeader" {
			err = decodeOSMHeader(blob)
		} else {
			err = fmt.Errorf("unexpected first fileblock of type %s", blobHeader.GetType())
		}
	}
	if err != nil {
		return err
	}

	// start data decoders
	for i := 0; i < n; i++ {
		input := make(chan pair)
		output := make(chan pair)
		go func() {
			dd := new(dataDecoder)
			for p := range input {
				if p.e == nil {
					// send decoded objects or decoding error
					objects, err := dd.Decode(p.i.(*OSMPBF.Blob))
					output <- pair{objects, err}
				} else {
					// send input error as is
					output <- pair{nil, p.e}
				}
			}
			close(output)
		}()

		dec.inputs = append(dec.inputs, input)
		dec.outputs = append(dec.outputs, output)
	}

	// start reading OSMData
	go func() {
		var inputIndex int
		for {
			input := dec.inputs[inputIndex]
			inputIndex = (inputIndex + 1) % n

			blobHeader, blob, err = dec.readFileBlock()
			if err == nil && blobHeader.GetType() != "OSMData" {
				err = fmt.Errorf("unexpected fileblock of type %s", blobHeader.GetType())
			}
			if err == nil {
				// send blob for decoding
				input <- pair{blob, nil}
			} else {
				// send input error as is
				input <- pair{nil, err}
				for _, input := range dec.inputs {
					close(input)
				}
				return
			}
		}
	}()

	go func() {
		var outputIndex int
		for {
			output := dec.outputs[outputIndex]
			outputIndex = (outputIndex + 1) % n

			p := <-output
			if p.i != nil {
				// send decoded objects one by one
				for _, o := range p.i.([]interface{}) {
					dec.serializer <- pair{o, nil}
				}
			}
			if p.e != nil {
				// send input or decoding error
				dec.serializer <- pair{nil, p.e}
				close(dec.serializer)
				return
			}
		}
	}()

	return nil
}

// Decode reads the next object from the input stream and returns either a
// Node, Way or Relation struct representing the underlying OpenStreetMap PBF
// data, or error encountered. The end of the input stream is reported by an io.EOF error.
//
// Decode is safe for parallel execution. Only first error encountered will be returned,
// subsequent invocations will return io.EOF.
func (dec *Decoder) Decode() (interface{}, error) {
	p, ok := <-dec.serializer
	if !ok {
		return nil, io.EOF
	}
	return p.i, p.e
}

func (dec *Decoder) readFileBlock() (*OSMPBF.BlobHeader, *OSMPBF.Blob, error) {
	blobHeaderSize, err := dec.readBlobHeaderSize()
	if err != nil {
		return nil, nil, err
	}

	blobHeader, err := dec.readBlobHeader(blobHeaderSize)
	if err != nil {
		return nil, nil, err
	}

	blob, err := dec.readBlob(blobHeader)
	if err != nil {
		return nil, nil, err
	}

	return blobHeader, blob, err
}

func (dec *Decoder) readBlobHeaderSize() (uint32, error) {
	buf := make([]byte, 4)
	if _, err := io.ReadFull(dec.r, buf); err != nil {
		return 0, err
	}
	size := binary.BigEndian.Uint32(buf)

	if size >= maxBlobHeaderSize {
		return 0, errors.New("BlobHeader size >= 64Kb")
	}
	return size, nil
}

func (dec *Decoder) readBlobHeader(size uint32) (*OSMPBF.BlobHeader, error) {
	buf := make([]byte, size)
	if _, err := io.ReadFull(dec.r, buf); err != nil {
		return nil, err
	}

	blobHeader := new(OSMPBF.BlobHeader)
	if err := proto.Unmarshal(buf, blobHeader); err != nil {
		return nil, err
	}

	if blobHeader.GetDatasize() >= maxBlobSize {
		return nil, errors.New("Blob size >= 32Mb")
	}
	return blobHeader, nil
}

func (dec *Decoder) readBlob(blobHeader *OSMPBF.BlobHeader) (*OSMPBF.Blob, error) {
	buf := make([]byte, blobHeader.GetDatasize())
	if _, err := io.ReadFull(dec.r, buf); err != nil {
		return nil, err
	}

	blob := new(OSMPBF.Blob)
	if err := proto.Unmarshal(buf, blob); err != nil {
		return nil, err
	}
	return blob, nil
}

func getData(blob *OSMPBF.Blob) ([]byte, error) {
	switch {
	case blob.Raw != nil:
		return blob.GetRaw(), nil

	case blob.ZlibData != nil:
		r, err := zlib.NewReader(bytes.NewReader(blob.GetZlibData()))
		if err != nil {
			return nil, err
		}
		buf := bytes.NewBuffer(make([]byte, 0, blob.GetRawSize()+bytes.MinRead))
		_, err = buf.ReadFrom(r)
		if err != nil {
			return nil, err
		}
		if buf.Len() != int(blob.GetRawSize()) {
			err = fmt.Errorf("raw blob data size %d but expected %d", buf.Len(), blob.GetRawSize())
			return nil, err
		}
		return buf.Bytes(), nil

	default:
		return nil, errors.New("unknown blob data")
	}
}

func decodeOSMHeader(blob *OSMPBF.Blob) error {
	data, err := getData(blob)
	if err != nil {
		return err
	}

	headerBlock := new(OSMPBF.HeaderBlock)
	if err := proto.Unmarshal(data, headerBlock); err != nil {
		return err
	}

	// Check we have the parse capabilities
	requiredFeatures := headerBlock.GetRequiredFeatures()
	for _, feature := range requiredFeatures {
		if !parseCapabilities[feature] {
			return fmt.Errorf("parser does not have %s capability", feature)
		}
	}

	return nil
}
<commit_msg>Update package documentation<commit_after>// Package osmpbf decodes OpenStreetMap (OSM) PBF files.
// Use this package by creating a NewDecoder and passing it a PBF file.
// Use Start to start decoding process.
// Use Decode to return Node, Way and Relation structs.
package osmpbf

import (
	"bytes"
	"code.google.com/p/goprotobuf/proto"
	"compress/zlib"
	"encoding/binary"
	"errors"
	"fmt"
	"github.com/qedus/osmpbf/OSMPBF"
	"io"
	"time"
)

const (
	maxBlobHeaderSize = 64 * 1024
	maxBlobSize       = 32 * 1024 * 1024
)

var (
	parseCapabilities = map[string]bool{
		"OsmSchema-V0.6": true,
		"DenseNodes":     true,
	}
)

type Node struct {
	ID        int64
	Lat       float64
	Lon       float64
	Tags      map[string]string
	Timestamp time.Time

	// TODO: Add more DenseInfo fields
}

type Way struct {
	ID        int64
	Tags      map[string]string
	NodeIDs   []int64
	Timestamp time.Time

	// TODO: Add more Info fields
}

type Relation struct {
	ID        int64
	Tags      map[string]string
	Members   []Member
	Timestamp time.Time

	// TODO: Add more Info fields
	// TODO: Add roles_sid
}

type MemberType int

const (
	NodeType MemberType = iota
	WayType
	RelationType
)

type Member struct {
	ID   int64
	Type MemberType
	Role string
}

type pair struct {
	i interface{}
	e error
}

// A Decoder reads and decodes OpenStreetMap PBF data from an input stream.
type Decoder struct {
	r          io.Reader
	serializer chan pair

	// for data decoders
	inputs  []chan<- pair
	outputs []<-chan pair
}

// NewDecoder returns a new decoder that reads from r.
func NewDecoder(r io.Reader) *Decoder {
	return &Decoder{
		r:          r,
		serializer: make(chan pair, 8000), // typical PrimitiveBlock contains 8k OSM entities
	}
}

// Start decoding process using n goroutines.
func (dec *Decoder) Start(n int) error {
	if n < 1 {
		n = 1
	}

	// read OSMHeader
	blobHeader, blob, err := dec.readFileBlock()
	if err == nil {
		if blobHeader.GetType() == "OSMHeader" {
			err = decodeOSMHeader(blob)
		} else {
			err = fmt.Errorf("unexpected first fileblock of type %s", blobHeader.GetType())
		}
	}
	if err != nil {
		return err
	}

	// start data decoders
	for i := 0; i < n; i++ {
		input := make(chan pair)
		output := make(chan pair)
		go func() {
			dd := new(dataDecoder)
			for p := range input {
				if p.e == nil {
					// send decoded objects or decoding error
					objects, err := dd.Decode(p.i.(*OSMPBF.Blob))
					output <- pair{objects, err}
				} else {
					// send input error as is
					output <- pair{nil, p.e}
				}
			}
			close(output)
		}()

		dec.inputs = append(dec.inputs, input)
		dec.outputs = append(dec.outputs, output)
	}

	// start reading OSMData
	go func() {
		var inputIndex int
		for {
			input := dec.inputs[inputIndex]
			inputIndex = (inputIndex + 1) % n

			blobHeader, blob, err = dec.readFileBlock()
			if err == nil && blobHeader.GetType() != "OSMData" {
				err = fmt.Errorf("unexpected fileblock of type %s", blobHeader.GetType())
			}
			if err == nil {
				// send blob for decoding
				input <- pair{blob, nil}
			} else {
				// send input error as is
				input <- pair{nil, err}
				for _, input := range dec.inputs {
					close(input)
				}
				return
			}
		}
	}()

	go func() {
		var outputIndex int
		for {
			output := dec.outputs[outputIndex]
			outputIndex = (outputIndex + 1) % n

			p := <-output
			if p.i != nil {
				// send decoded objects one by one
				for _, o := range p.i.([]interface{}) {
					dec.serializer <- pair{o, nil}
				}
			}
			if p.e != nil {
				// send input or decoding error
				dec.serializer <- pair{nil, p.e}
				close(dec.serializer)
				return
			}
		}
	}()

	return nil
}

// Decode reads the next object from the input stream and returns either a
// Node, Way or Relation struct representing the underlying OpenStreetMap PBF
// data, or error encountered. The end of the input stream is reported by an io.EOF error.
//
// Decode is safe for parallel execution. Only first error encountered will be returned,
// subsequent invocations will return io.EOF.
func (dec *Decoder) Decode() (interface{}, error) {
	p, ok := <-dec.serializer
	if !ok {
		return nil, io.EOF
	}
	return p.i, p.e
}

func (dec *Decoder) readFileBlock() (*OSMPBF.BlobHeader, *OSMPBF.Blob, error) {
	blobHeaderSize, err := dec.readBlobHeaderSize()
	if err != nil {
		return nil, nil, err
	}

	blobHeader, err := dec.readBlobHeader(blobHeaderSize)
	if err != nil {
		return nil, nil, err
	}

	blob, err := dec.readBlob(blobHeader)
	if err != nil {
		return nil, nil, err
	}

	return blobHeader, blob, err
}

func (dec *Decoder) readBlobHeaderSize() (uint32, error) {
	buf := make([]byte, 4)
	if _, err := io.ReadFull(dec.r, buf); err != nil {
		return 0, err
	}
	size := binary.BigEndian.Uint32(buf)

	if size >= maxBlobHeaderSize {
		return 0, errors.New("BlobHeader size >= 64Kb")
	}
	return size, nil
}

func (dec *Decoder) readBlobHeader(size uint32) (*OSMPBF.BlobHeader, error) {
	buf := make([]byte, size)
	if _, err := io.ReadFull(dec.r, buf); err != nil {
		return nil, err
	}

	blobHeader := new(OSMPBF.BlobHeader)
	if err := proto.Unmarshal(buf, blobHeader); err != nil {
		return nil, err
	}

	if blobHeader.GetDatasize() >= maxBlobSize {
		return nil, errors.New("Blob size >= 32Mb")
	}
	return blobHeader, nil
}

func (dec *Decoder) readBlob(blobHeader *OSMPBF.BlobHeader) (*OSMPBF.Blob, error) {
	buf := make([]byte, blobHeader.GetDatasize())
	if _, err := io.ReadFull(dec.r, buf); err != nil {
		return nil, err
	}

	blob := new(OSMPBF.Blob)
	if err := proto.Unmarshal(buf, blob); err != nil {
		return nil, err
	}
	return blob, nil
}

func getData(blob *OSMPBF.Blob) ([]byte, error) {
	switch {
	case blob.Raw != nil:
		return blob.GetRaw(), nil

	case blob.ZlibData != nil:
		r, err := zlib.NewReader(bytes.NewReader(blob.GetZlibData()))
		if err != nil {
			return nil, err
		}
		buf := bytes.NewBuffer(make([]byte, 0, blob.GetRawSize()+bytes.MinRead))
		_, err = buf.ReadFrom(r)
		if err != nil {
			return nil, err
		}
		if buf.Len() != int(blob.GetRawSize()) {
			err = fmt.Errorf("raw blob data size %d but expected %d", buf.Len(), blob.GetRawSize())
			return nil, err
		}
		return buf.Bytes(), nil

	default:
		return nil, errors.New("unknown blob data")
	}
}

func decodeOSMHeader(blob *OSMPBF.Blob) error {
	data, err := getData(blob)
	if err != nil {
		return err
	}

	headerBlock := new(OSMPBF.HeaderBlock)
	if err := proto.Unmarshal(data, headerBlock); err != nil {
		return err
	}

	// Check we have the parse capabilities
	requiredFeatures := headerBlock.GetRequiredFeatures()
	for _, feature := range requiredFeatures {
		if !parseCapabilities[feature] {
			return fmt.Errorf("parser does not have %s capability", feature)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>// Package appsettings provides simple key/value store functionality
package appsettings

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
	"sync"
)

type DataTree map[string]string

type dataStruct struct {
	Tree map[string]DataTree
}

type AppSettings struct {
	filename string
	data     dataStruct

	sync.Mutex
}

func NewAppSettings(dbFilename string) (*AppSettings, error) {
	var data dataStruct
	if _, err := os.Stat(dbFilename); os.IsNotExist(err) {
		data = dataStruct{
			Tree: make(map[string]DataTree),
		}

		d1, _ := json.Marshal(data)
		err := ioutil.WriteFile(dbFilename, d1, 0644)
		if err != nil {
			return nil, err
		}
	} else {
		d1, err := ioutil.ReadFile(dbFilename)
		if err != nil {
			return nil, err
		}

		err = json.Unmarshal(d1, &data)
		if err != nil {
			return nil, err
		}
	}

	return &AppSettings{filename: dbFilename, data: data}, nil
}

func (a *DataTree) GetString(key string) (string, error) {
	y := *a
	if _, ok := y[key]; !ok {
		return "", fmt.Errorf("undefined key %s", key)
	}

	return y[key], nil
}

func (a *DataTree) SetString(key string, val string) {
	y := *a
	y[key] = val
}

func (a *DataTree) GetInt(key string) (int, error) {
	str, err := a.GetString(key)
	if err != nil {
		return 0, err
	}

	i, err := strconv.Atoi(str)
	if err != nil {
		return 0, err
	}
	return i, nil
}

func (a *DataTree) SetInt(key string, val int) {
	y := *a
	y[key] = strconv.Itoa(val)
}

func (a *DataTree) GetInt64(key string) (int64, error) {
	str, err := a.GetString(key)
	if err != nil {
		return 0, err
	}

	i, err := strconv.ParseInt(str, 10, 64)
	if err != nil {
		return 0, err
	}
	return i, nil
}

func (a *DataTree) SetInt64(key string, val int64) {
	y := *a
	y[key] = strconv.FormatInt(val, 10)
}

func (a *DataTree) Delete(key string) {
	delete(*a, key)
}

func (a *AppSettings) GetTree(key string) DataTree {
	a.Lock()
	defer a.Unlock()

	if _, ok := a.data.Tree[key]; !ok {
		a.data.Tree[key] = make(DataTree)
	}

	return a.data.Tree[key]
}

// Persist causes the current state of the app settings to be persisted.
func (a *AppSettings) Persist() error {
	a.Lock()
	defer a.Unlock()

	d1, _ := json.Marshal(a.data)
	err := ioutil.WriteFile(a.filename, d1, 0644)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Refactor to allow nested trees<commit_after>// Package appsettings provides simple key/value store functionality
package appsettings

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"os"
	"strconv"
	"sync"
)

// DataTree is the host of key and branches of values
type DataTree interface {
	GetString(key string) (string, error)
	SetString(key string, val string)
	GetInt(key string) (int, error)
	SetInt(key string, val int)
	GetInt64(key string) (int64, error)
	SetInt64(key string, val int64)
	Delete(key string)
	GetTree(key string) DataTree

	GetLeaves() map[string]string
}

type tree struct {
	Branches map[string]*tree
	Leaves   map[string]string

	sync.Mutex
}

// AppSettings is the root most DataTree
type AppSettings struct {
	filename string

	*tree
}

// NewAppSettings gets a new AppSettings struct
func NewAppSettings(dbFilename string) (*AppSettings, error) {
	a := &AppSettings{
		filename: dbFilename,
		tree: &tree{
			Branches: make(map[string]*tree),
			Leaves:   make(map[string]string),
		},
	}

	if _, err := os.Stat(dbFilename); os.IsNotExist(err) {
		d1, _ := json.Marshal(a)
		err := ioutil.WriteFile(dbFilename, d1, 0644)
		if err != nil {
			return nil, err
		}
	} else {
		d1, err := ioutil.ReadFile(dbFilename)
		if err != nil {
			return nil, err
		}

		err = json.Unmarshal(d1, a)
		if err != nil {
			return nil, err
		}
	}

	return a, nil
}

// ErrUndefinedKey is returned when the key requested from get is undefined.
var ErrUndefinedKey = errors.New("undefined key")

func (a *tree) GetString(key string) (string, error) {
	if _, ok := a.Leaves[key]; !ok {
		return "", ErrUndefinedKey
	}

	return a.Leaves[key], nil
}

func (a *tree) SetString(key string, val string) {
	a.Leaves[key] = val
}

func (a *tree) GetInt(key string) (int, error) {
	str, err := a.GetString(key)
	if err != nil {
		return 0, err
	}

	i, err := strconv.Atoi(str)
	if err != nil {
		return 0, err
	}
	return i, nil
}

func (a *tree) SetInt(key string, val int) {
	a.Leaves[key] = strconv.Itoa(val)
}

func (a *tree) GetInt64(key string) (int64, error) {
	str, err := a.GetString(key)
	if err != nil {
		return 0, err
	}

	i, err := strconv.ParseInt(str, 10, 64)
	if err != nil {
		return 0, err
	}
	return i, nil
}

func (a *tree) SetInt64(key string, val int64) {
	// y := *a
	a.Leaves[key] = strconv.FormatInt(val, 10)
}

func (a *tree) Delete(key string) {
	delete(a.Leaves, key)
}

func (a *tree) GetLeaves() map[string]string {
	return a.Leaves
}

// GetTree fetches a tree for app setting storage
func (a *tree) GetTree(key string) DataTree {
	// a.Lock()
	// defer a.Unlock()

	if _, ok := a.Branches[key]; !ok {
		a.Branches[key] = &tree{
			Branches: make(map[string]*tree),
			Leaves:   make(map[string]string),
		}
	}

	return a.Branches[key]
}

// Persist causes the current state of the app settings to be persisted.
func (a *AppSettings) Persist() error {
	a.Lock()
	defer a.Unlock()

	d1, _ := json.Marshal(a)
	err := ioutil.WriteFile(a.filename, d1, 0644)
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>// 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 generator

// microgenConfig represents a single microgen target.
type microgenConfig struct {
	// inputDirectoryPath is the path to the input (.proto, etc) files, relative
	// to googleapisDir.
	inputDirectoryPath string

	// importPath is the path that this library should be imported as.
	importPath string

	// pkg is the name that should be used in the package declaration.
	pkg string

	// gRPCServiceConfigPath is the path to the grpc service config for this
	// target, relative to googleapisDir.
	gRPCServiceConfigPath string

	// apiServiceConfigPath is the path to the gapic service config for this
	// target, relative to googleapisDir.
	apiServiceConfigPath string

	// releaseLevel is the release level of this target. Values incl ga,
	// beta, alpha.
	releaseLevel string
}

var microgenGapicConfigs = []*microgenConfig{
	{
		inputDirectoryPath:    "google/cloud/texttospeech/v1",
		pkg:                   "texttospeech",
		importPath:            "cloud.google.com/go/texttospeech/apiv1",
		gRPCServiceConfigPath: "google/cloud/texttospeech/v1/texttospeech_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/texttospeech/v1/texttospeech_v1.yaml",
		releaseLevel:          "alpha",
	},
	{
		inputDirectoryPath:    "google/cloud/asset/v1",
		pkg:                   "asset",
		importPath:            "cloud.google.com/go/asset/apiv1",
		gRPCServiceConfigPath: "google/cloud/asset/v1/cloudasset_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/asset/v1/cloudasset_v1.yaml",
		releaseLevel:          "alpha",
	},
	{
		inputDirectoryPath:    "google/cloud/language/v1",
		pkg:                   "language",
		importPath:            "cloud.google.com/go/language/apiv1",
		gRPCServiceConfigPath: "google/cloud/language/v1/language_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/language/language_v1.yaml",
		releaseLevel:          "alpha",
	},
	{
		inputDirectoryPath:    "google/cloud/phishingprotection/v1beta1",
		pkg:                   "phishingprotection",
		importPath:            "cloud.google.com/go/phishingprotection/apiv1beta1",
		gRPCServiceConfigPath: "google/cloud/phishingprotection/v1beta1/phishingprotection_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/phishingprotection/v1beta1/phishingprotection_v1beta1.yaml",
		releaseLevel:          "beta",
	},
	{
		inputDirectoryPath:    "google/cloud/translate/v3",
		pkg:                   "translate",
		importPath:            "cloud.google.com/go/translate/apiv3",
		gRPCServiceConfigPath: "google/cloud/translate/v3/translate_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/translate/v3/translate_v3.yaml",
		releaseLevel:          "ga",
	},
	{
		inputDirectoryPath:    "google/cloud/scheduler/v1",
		pkg:                   "scheduler",
		importPath:            "cloud.google.com/go/scheduler/apiv1",
		gRPCServiceConfigPath: "google/cloud/scheduler/v1/cloudscheduler_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/scheduler/v1/cloudscheduler_v1.yaml",
		releaseLevel:          "ga",
	},
	{
		inputDirectoryPath:    "google/cloud/scheduler/v1beta1",
		pkg:                   "scheduler",
		importPath:            "cloud.google.com/go/scheduler/apiv1beta1",
		gRPCServiceConfigPath: "google/cloud/scheduler/v1beta1/cloudscheduler_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/scheduler/v1beta1/cloudscheduler_v1beta1.yaml",
		releaseLevel:          "beta",
	},
	{
		inputDirectoryPath:    "google/cloud/speech/v1",
		pkg:                   "speech",
		importPath:            "cloud.google.com/go/speech/apiv1",
		gRPCServiceConfigPath: "google/cloud/speech/v1/speech_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/speech/v1/speech_v1.yaml",
		releaseLevel:          "ga",
	},
	{
		inputDirectoryPath:    "google/cloud/speech/v1p1beta1",
		pkg:                   "speech",
		importPath:            "cloud.google.com/go/speech/apiv1p1beta1",
		gRPCServiceConfigPath: "google/cloud/speech/v1p1beta1/speech_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/speech/v1p1beta1/speech_v1p1beta1.yaml",
		releaseLevel:          "beta",
	},
	{
		inputDirectoryPath:    "google/cloud/bigquery/datatransfer/v1",
		pkg:                   "datatransfer",
		importPath:            "cloud.google.com/go/bigquery/datatransfer/apiv1",
		gRPCServiceConfigPath: "google/cloud/bigquery/datatransfer/v1/bigquerydatatransfer_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/bigquery/datatransfer/v1/bigquerydatatransfer_v1.yaml",
		releaseLevel:          "alpha",
	},
	{
		inputDirectoryPath:    "google/cloud/bigquery/storage/v1beta1",
		pkg:                   "storage",
		importPath:            "cloud.google.com/go/bigquery/storage/apiv1beta1",
		gRPCServiceConfigPath: "google/cloud/bigquery/storage/v1beta1/bigquerystorage_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/bigquery/storage/v1beta1/bigquerystorage_v1beta1.yaml",
		releaseLevel:          "beta",
	},
	{
		inputDirectoryPath:    "google/cloud/iot/v1",
		pkg:                   "iot",
		importPath:            "cloud.google.com/go/iot/apiv1",
		gRPCServiceConfigPath: "google/cloud/iot/v1/cloudiot_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/iot/v1/cloudiot_v1.yaml",
		releaseLevel:          "ga",
	},
	{
		inputDirectoryPath:    "google/cloud/recommender/v1beta1",
		pkg:                   "recommender",
		importPath:            "cloud.google.com/go/recommender/apiv1beta1",
		gRPCServiceConfigPath: "google/cloud/recommender/v1beta1/recommender_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/recommender/v1beta1/recommender_v1beta1.yaml",
		releaseLevel:          "beta",
	},
	{
		inputDirectoryPath:    "google/cloud/tasks/v2",
		pkg:                   "cloudtasks",
		importPath:            "cloud.google.com/go/cloudtasks/apiv2",
		gRPCServiceConfigPath: "google/cloud/tasks/v2/cloudtasks_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/tasks/v2/cloudtasks_v2.yaml",
		releaseLevel:          "ga",
	},
	{
		inputDirectoryPath:    "google/cloud/tasks/v2beta2",
		pkg:                   "cloudtasks",
		importPath:            "cloud.google.com/go/cloudtasks/apiv2beta2",
		gRPCServiceConfigPath: "google/cloud/tasks/v2beta2/cloudtasks_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/tasks/v2beta2/cloudtasks_v2beta2.yaml",
		releaseLevel:          "beta",
	},
	{
		inputDirectoryPath:    "google/cloud/tasks/v2beta3",
		pkg:                   "cloudtasks",
		importPath:            "cloud.google.com/go/cloudtasks/apiv2beta3",
		gRPCServiceConfigPath: "google/cloud/tasks/v2beta3/cloudtasks_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/tasks/v2beta3/cloudtasks_v2beta3.yaml",
		releaseLevel:          "beta",
	},
	{
		inputDirectoryPath:    "google/cloud/videointelligence/v1",
		pkg:                   "videointelligence",
		importPath:            "cloud.google.com/go/videointelligence/apiv1",
		gRPCServiceConfigPath: "google/cloud/videointelligence/v1/videointelligence_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/videointelligence/v1/videointelligence_v1.yaml",
		releaseLevel:          "alpha",
	},
	{
		inputDirectoryPath:    "google/cloud/vision/v1",
		pkg:                   "vision",
		importPath:            "cloud.google.com/go/vision/apiv1",
		gRPCServiceConfigPath: "google/cloud/vision/v1/vision_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/vision/v1/vision_v1.yaml",
		releaseLevel:          "ga",
	},
	{
		inputDirectoryPath:    "google/cloud/webrisk/v1beta1",
		pkg:                   "webrisk",
		importPath:            "cloud.google.com/go/webrisk/apiv1beta1",
		gRPCServiceConfigPath: "google/cloud/webrisk/v1beta1/webrisk_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/webrisk/v1beta1/webrisk_v1beta1.yaml",
		releaseLevel:          "beta",
	},
}

// Relative to gocloud dir.
var gapicsWithManual = []string{
	"errorreporting/apiv1beta1",
	"firestore/apiv1beta1",
	"firestore/apiv1",
	"logging/apiv2",
	"longrunning/autogen",
	"pubsub/apiv1",
	"spanner/apiv1",
	"trace/apiv1",
}

// Relative to googleapis dir.
var artmanGapicConfigPaths = []string{
	"google/api/expr/artman_cel.yaml",
	"google/cloud/asset/artman_cloudasset_v1beta1.yaml",
	"google/cloud/asset/artman_cloudasset_v1p2beta1.yaml",
	"google/iam/credentials/artman_iamcredentials_v1.yaml",
	"google/cloud/automl/artman_automl_v1.yaml",
	"google/cloud/automl/artman_automl_v1beta1.yaml",
	"google/cloud/dataproc/artman_dataproc_v1.yaml",
	"google/cloud/dataproc/artman_dataproc_v1beta2.yaml",
	"google/cloud/dialogflow/v2/artman_dialogflow_v2.yaml",
	"google/cloud/irm/artman_irm_v1alpha2.yaml",
	"google/cloud/kms/artman_cloudkms.yaml",
	"google/cloud/language/artman_language_v1beta2.yaml",
	"google/cloud/oslogin/artman_oslogin_v1.yaml",
	"google/cloud/oslogin/artman_oslogin_v1beta.yaml",
	"google/cloud/recaptchaenterprise/artman_recaptchaenterprise_v1beta1.yaml",
	"google/cloud/redis/artman_redis_v1beta1.yaml",
	"google/cloud/redis/artman_redis_v1.yaml",
	"google/cloud/securitycenter/artman_securitycenter_v1beta1.yaml",
	"google/cloud/securitycenter/artman_securitycenter_v1.yaml",
	"google/cloud/talent/artman_talent_v4beta1.yaml",
	"google/cloud/videointelligence/artman_videointelligence_v1beta2.yaml",
	"google/cloud/vision/artman_vision_v1p1beta1.yaml",
	"google/devtools/artman_clouddebugger.yaml",
	"google/devtools/cloudbuild/artman_cloudbuild.yaml",
	"google/devtools/clouderrorreporting/artman_errorreporting.yaml",
	"google/devtools/cloudtrace/artman_cloudtrace_v1.yaml",
	"google/devtools/cloudtrace/artman_cloudtrace_v2.yaml",
	"google/devtools/containeranalysis/artman_containeranalysis_v1beta1.yaml",
	"google/firestore/artman_firestore.yaml",
	"google/firestore/admin/artman_firestore_v1.yaml",
	"google/logging/artman_logging.yaml",
	"google/longrunning/artman_longrunning.yaml",
	"google/monitoring/artman_monitoring.yaml",
	"google/privacy/dlp/artman_dlp_v2.yaml",
	"google/pubsub/artman_pubsub.yaml",
	"google/spanner/admin/database/artman_spanner_admin_database.yaml",
	"google/spanner/admin/instance/artman_spanner_admin_instance.yaml",
	"google/spanner/artman_spanner.yaml",
}
<commit_msg>secretmanager: add secretmanager to gen config<commit_after>// 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 generator

// microgenConfig represents a single microgen target.
type microgenConfig struct {
	// inputDirectoryPath is the path to the input (.proto, etc) files, relative
	// to googleapisDir.
	inputDirectoryPath string

	// importPath is the path that this library should be imported as.
	importPath string

	// pkg is the name that should be used in the package declaration.
	pkg string

	// gRPCServiceConfigPath is the path to the grpc service config for this
	// target, relative to googleapisDir.
	gRPCServiceConfigPath string

	// apiServiceConfigPath is the path to the gapic service config for this
	// target, relative to googleapisDir.
	apiServiceConfigPath string

	// releaseLevel is the release level of this target. Values incl ga,
	// beta, alpha.
	releaseLevel string
}

var microgenGapicConfigs = []*microgenConfig{
	{
		inputDirectoryPath:    "google/cloud/texttospeech/v1",
		pkg:                   "texttospeech",
		importPath:            "cloud.google.com/go/texttospeech/apiv1",
		gRPCServiceConfigPath: "google/cloud/texttospeech/v1/texttospeech_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/texttospeech/v1/texttospeech_v1.yaml",
		releaseLevel:          "alpha",
	},
	{
		inputDirectoryPath:    "google/cloud/asset/v1",
		pkg:                   "asset",
		importPath:            "cloud.google.com/go/asset/apiv1",
		gRPCServiceConfigPath: "google/cloud/asset/v1/cloudasset_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/asset/v1/cloudasset_v1.yaml",
		releaseLevel:          "alpha",
	},
	{
		inputDirectoryPath:    "google/cloud/language/v1",
		pkg:                   "language",
		importPath:            "cloud.google.com/go/language/apiv1",
		gRPCServiceConfigPath: "google/cloud/language/v1/language_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/language/language_v1.yaml",
		releaseLevel:          "alpha",
	},
	{
		inputDirectoryPath:    "google/cloud/phishingprotection/v1beta1",
		pkg:                   "phishingprotection",
		importPath:            "cloud.google.com/go/phishingprotection/apiv1beta1",
		gRPCServiceConfigPath: "google/cloud/phishingprotection/v1beta1/phishingprotection_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/phishingprotection/v1beta1/phishingprotection_v1beta1.yaml",
		releaseLevel:          "beta",
	},
	{
		inputDirectoryPath:    "google/cloud/translate/v3",
		pkg:                   "translate",
		importPath:            "cloud.google.com/go/translate/apiv3",
		gRPCServiceConfigPath: "google/cloud/translate/v3/translate_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/translate/v3/translate_v3.yaml",
		releaseLevel:          "ga",
	},
	{
		inputDirectoryPath:    "google/cloud/scheduler/v1",
		pkg:                   "scheduler",
		importPath:            "cloud.google.com/go/scheduler/apiv1",
		gRPCServiceConfigPath: "google/cloud/scheduler/v1/cloudscheduler_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/scheduler/v1/cloudscheduler_v1.yaml",
		releaseLevel:          "ga",
	},
	{
		inputDirectoryPath:    "google/cloud/scheduler/v1beta1",
		pkg:                   "scheduler",
		importPath:            "cloud.google.com/go/scheduler/apiv1beta1",
		gRPCServiceConfigPath: "google/cloud/scheduler/v1beta1/cloudscheduler_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/scheduler/v1beta1/cloudscheduler_v1beta1.yaml",
		releaseLevel:          "beta",
	},
	{
		inputDirectoryPath:    "google/cloud/speech/v1",
		pkg:                   "speech",
		importPath:            "cloud.google.com/go/speech/apiv1",
		gRPCServiceConfigPath: "google/cloud/speech/v1/speech_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/speech/v1/speech_v1.yaml",
		releaseLevel:          "ga",
	},
	{
		inputDirectoryPath:    "google/cloud/speech/v1p1beta1",
		pkg:                   "speech",
		importPath:            "cloud.google.com/go/speech/apiv1p1beta1",
		gRPCServiceConfigPath: "google/cloud/speech/v1p1beta1/speech_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/speech/v1p1beta1/speech_v1p1beta1.yaml",
		releaseLevel:          "beta",
	},
	{
		inputDirectoryPath:    "google/cloud/bigquery/datatransfer/v1",
		pkg:                   "datatransfer",
		importPath:            "cloud.google.com/go/bigquery/datatransfer/apiv1",
		gRPCServiceConfigPath: "google/cloud/bigquery/datatransfer/v1/bigquerydatatransfer_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/bigquery/datatransfer/v1/bigquerydatatransfer_v1.yaml",
		releaseLevel:          "alpha",
	},
	{
		inputDirectoryPath:    "google/cloud/bigquery/storage/v1beta1",
		pkg:                   "storage",
		importPath:            "cloud.google.com/go/bigquery/storage/apiv1beta1",
		gRPCServiceConfigPath: "google/cloud/bigquery/storage/v1beta1/bigquerystorage_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/bigquery/storage/v1beta1/bigquerystorage_v1beta1.yaml",
		releaseLevel:          "beta",
	},
	{
		inputDirectoryPath:    "google/cloud/iot/v1",
		pkg:                   "iot",
		importPath:            "cloud.google.com/go/iot/apiv1",
		gRPCServiceConfigPath: "google/cloud/iot/v1/cloudiot_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/iot/v1/cloudiot_v1.yaml",
		releaseLevel:          "ga",
	},
	{
		inputDirectoryPath:    "google/cloud/recommender/v1beta1",
		pkg:                   "recommender",
		importPath:            "cloud.google.com/go/recommender/apiv1beta1",
		gRPCServiceConfigPath: "google/cloud/recommender/v1beta1/recommender_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/recommender/v1beta1/recommender_v1beta1.yaml",
		releaseLevel:          "beta",
	},
	{
		inputDirectoryPath:    "google/cloud/tasks/v2",
		pkg:                   "cloudtasks",
		importPath:            "cloud.google.com/go/cloudtasks/apiv2",
		gRPCServiceConfigPath: "google/cloud/tasks/v2/cloudtasks_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/tasks/v2/cloudtasks_v2.yaml",
		releaseLevel:          "ga",
	},
	{
		inputDirectoryPath:    "google/cloud/tasks/v2beta2",
		pkg:                   "cloudtasks",
		importPath:            "cloud.google.com/go/cloudtasks/apiv2beta2",
		gRPCServiceConfigPath: "google/cloud/tasks/v2beta2/cloudtasks_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/tasks/v2beta2/cloudtasks_v2beta2.yaml",
		releaseLevel:          "beta",
	},
	{
		inputDirectoryPath:    "google/cloud/tasks/v2beta3",
		pkg:                   "cloudtasks",
		importPath:            "cloud.google.com/go/cloudtasks/apiv2beta3",
		gRPCServiceConfigPath: "google/cloud/tasks/v2beta3/cloudtasks_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/tasks/v2beta3/cloudtasks_v2beta3.yaml",
		releaseLevel:          "beta",
	},
	{
		inputDirectoryPath:    "google/cloud/videointelligence/v1",
		pkg:                   "videointelligence",
		importPath:            "cloud.google.com/go/videointelligence/apiv1",
		gRPCServiceConfigPath: "google/cloud/videointelligence/v1/videointelligence_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/videointelligence/v1/videointelligence_v1.yaml",
		releaseLevel:          "alpha",
	},
	{
		inputDirectoryPath:    "google/cloud/vision/v1",
		pkg:                   "vision",
		importPath:            "cloud.google.com/go/vision/apiv1",
		gRPCServiceConfigPath: "google/cloud/vision/v1/vision_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/vision/v1/vision_v1.yaml",
		releaseLevel:          "ga",
	},
	{
		inputDirectoryPath:    "google/cloud/webrisk/v1beta1",
		pkg:                   "webrisk",
		importPath:            "cloud.google.com/go/webrisk/apiv1beta1",
		gRPCServiceConfigPath: "google/cloud/webrisk/v1beta1/webrisk_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/webrisk/v1beta1/webrisk_v1beta1.yaml",
		releaseLevel:          "beta",
	},
	{
		inputDirectoryPath:    "google/cloud/secrets/v1beta1",
		pkg:                   "secretmanager",
		importPath:            "cloud.google.com/go/secretmanager/apiv1beta1",
		gRPCServiceConfigPath: "google/cloud/secrets/v1beta1/secretmanager_grpc_service_config.json",
		apiServiceConfigPath:  "google/cloud/secrets/v1beta1/secretmanager_v1beta1.yaml",
		releaseLevel:          "beta",
	},
}

// Relative to gocloud dir.
var gapicsWithManual = []string{
	"errorreporting/apiv1beta1",
	"firestore/apiv1beta1",
	"firestore/apiv1",
	"logging/apiv2",
	"longrunning/autogen",
	"pubsub/apiv1",
	"spanner/apiv1",
	"trace/apiv1",
}

// Relative to googleapis dir.
var artmanGapicConfigPaths = []string{
	"google/api/expr/artman_cel.yaml",
	"google/cloud/asset/artman_cloudasset_v1beta1.yaml",
	"google/cloud/asset/artman_cloudasset_v1p2beta1.yaml",
	"google/iam/credentials/artman_iamcredentials_v1.yaml",
	"google/cloud/automl/artman_automl_v1.yaml",
	"google/cloud/automl/artman_automl_v1beta1.yaml",
	"google/cloud/dataproc/artman_dataproc_v1.yaml",
	"google/cloud/dataproc/artman_dataproc_v1beta2.yaml",
	"google/cloud/dialogflow/v2/artman_dialogflow_v2.yaml",
	"google/cloud/irm/artman_irm_v1alpha2.yaml",
	"google/cloud/kms/artman_cloudkms.yaml",
	"google/cloud/language/artman_language_v1beta2.yaml",
	"google/cloud/oslogin/artman_oslogin_v1.yaml",
	"google/cloud/oslogin/artman_oslogin_v1beta.yaml",
	"google/cloud/recaptchaenterprise/artman_recaptchaenterprise_v1beta1.yaml",
	"google/cloud/redis/artman_redis_v1beta1.yaml",
	"google/cloud/redis/artman_redis_v1.yaml",
	"google/cloud/securitycenter/artman_securitycenter_v1beta1.yaml",
	"google/cloud/securitycenter/artman_securitycenter_v1.yaml",
	"google/cloud/talent/artman_talent_v4beta1.yaml",
	"google/cloud/videointelligence/artman_videointelligence_v1beta2.yaml",
	"google/cloud/vision/artman_vision_v1p1beta1.yaml",
	"google/devtools/artman_clouddebugger.yaml",
	"google/devtools/cloudbuild/artman_cloudbuild.yaml",
	"google/devtools/clouderrorreporting/artman_errorreporting.yaml",
	"google/devtools/cloudtrace/artman_cloudtrace_v1.yaml",
	"google/devtools/cloudtrace/artman_cloudtrace_v2.yaml",
	"google/devtools/containeranalysis/artman_containeranalysis_v1beta1.yaml",
	"google/firestore/artman_firestore.yaml",
	"google/firestore/admin/artman_firestore_v1.yaml",
	"google/logging/artman_logging.yaml",
	"google/longrunning/artman_longrunning.yaml",
	"google/monitoring/artman_monitoring.yaml",
	"google/privacy/dlp/artman_dlp_v2.yaml",
	"google/pubsub/artman_pubsub.yaml",
	"google/spanner/admin/database/artman_spanner_admin_database.yaml",
	"google/spanner/admin/instance/artman_spanner_admin_instance.yaml",
	"google/spanner/artman_spanner.yaml",
}
<|endoftext|>


<commit_before>package provider

import (
	"context"
	"fmt"
	"strconv"
	"strings"

	"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func resourceInteger() *schema.Resource {
	return &schema.Resource{
		Description: "The resource `random_integer` generates random values from a given range, described " +
			"by the `min` and `max` attributes of a given resource.\n" +
			"\n" +
			"This resource can be used in conjunction with resources that have the `create_before_destroy` " +
			"lifecycle flag set, to avoid conflicts with unique names during the brief period where both the " +
			"old and new resources exist concurrently.",
		CreateContext: CreateInteger,
		ReadContext:   schema.NoopContext,
		DeleteContext: RemoveResourceFromState,
		Importer: &schema.ResourceImporter{
			StateContext: ImportInteger,
		},

		Schema: map[string]*schema.Schema{
			"keepers": {
				Description: "Arbitrary map of values that, when changed, will trigger recreation of " +
					"resource. See [the main provider documentation](../index.html) for more information.",
				Type:     schema.TypeMap,
				Optional: true,
				ForceNew: true,
			},

			"min": {
				Description: "The minimum inclusive value of the range.",
				Type:        schema.TypeInt,
				Required:    true,
				ForceNew:    true,
			},

			"max": {
				Description: "The maximum inclusive value of the range.",
				Type:        schema.TypeInt,
				Required:    true,
				ForceNew:    true,
			},

			"seed": {
				Description: "A custom seed to always produce the same value.",
				Type:        schema.TypeString,
				Optional:    true,
				ForceNew:    true,
			},

			"result": {
				Description: "The random integer result.",
				Type:        schema.TypeInt,
				Computed:    true,
			},

			"id": {
				Description: "The string representation of the integer result.",
				Type:        schema.TypeString,
				Computed:    true,
			},
		},
		UseJSONNumber: true,
	}
}

func CreateInteger(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
	var diags diag.Diagnostics
	min := d.Get("min").(int)
	max := d.Get("max").(int)
	seed := d.Get("seed").(string)

	if max <= min {
		return append(diags, diag.Diagnostic{
			Severity: diag.Error,
			Summary:  "minimum value needs to be smaller than maximum value",
		})
	}
	rand := NewRand(seed)
	number := rand.Intn((max+1)-min) + min

	if err := d.Set("result", number); err != nil {
		return diag.Errorf("error setting result: %s", err)
	}

	d.SetId(strconv.Itoa(number))

	return nil
}

func ImportInteger(_ context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
	parts := strings.Split(d.Id(), ",")
	if len(parts) != 3 && len(parts) != 4 {
		return nil, fmt.Errorf("Invalid import usage: expecting {result},{min},{max} or {result},{min},{max},{seed}")
	}

	result, err := strconv.Atoi(parts[0])
	if err != nil {
		return nil, fmt.Errorf("error parsing result: %w", err)
	}

	if err := d.Set("result", result); err != nil {
		return nil, fmt.Errorf("error setting result: %w", err)
	}

	min, err := strconv.Atoi(parts[1])
	if err != nil {
		return nil, fmt.Errorf("error parsing min: %w", err)
	}

	if err := d.Set("min", min); err != nil {
		return nil, fmt.Errorf("error setting min: %w", err)
	}

	max, err := strconv.Atoi(parts[2])
	if err != nil {
		return nil, fmt.Errorf("error parsing max: %w", err)
	}

	if err := d.Set("max", max); err != nil {
		return nil, fmt.Errorf("error setting max: %w", err)
	}

	if len(parts) == 4 {
		if err := d.Set("seed", parts[3]); err != nil {
			return nil, fmt.Errorf("error setting seed: %w", err)
		}
	}

	d.SetId(parts[0])

	return []*schema.ResourceData{d}, nil
}
<commit_msg>accept min==max for random_integer resource<commit_after>package provider

import (
	"context"
	"fmt"
	"strconv"
	"strings"

	"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func resourceInteger() *schema.Resource {
	return &schema.Resource{
		Description: "The resource `random_integer` generates random values from a given range, described " +
			"by the `min` and `max` attributes of a given resource.\n" +
			"\n" +
			"This resource can be used in conjunction with resources that have the `create_before_destroy` " +
			"lifecycle flag set, to avoid conflicts with unique names during the brief period where both the " +
			"old and new resources exist concurrently.",
		CreateContext: CreateInteger,
		ReadContext:   schema.NoopContext,
		DeleteContext: RemoveResourceFromState,
		Importer: &schema.ResourceImporter{
			StateContext: ImportInteger,
		},

		Schema: map[string]*schema.Schema{
			"keepers": {
				Description: "Arbitrary map of values that, when changed, will trigger recreation of " +
					"resource. See [the main provider documentation](../index.html) for more information.",
				Type:     schema.TypeMap,
				Optional: true,
				ForceNew: true,
			},

			"min": {
				Description: "The minimum inclusive value of the range.",
				Type:        schema.TypeInt,
				Required:    true,
				ForceNew:    true,
			},

			"max": {
				Description: "The maximum inclusive value of the range.",
				Type:        schema.TypeInt,
				Required:    true,
				ForceNew:    true,
			},

			"seed": {
				Description: "A custom seed to always produce the same value.",
				Type:        schema.TypeString,
				Optional:    true,
				ForceNew:    true,
			},

			"result": {
				Description: "The random integer result.",
				Type:        schema.TypeInt,
				Computed:    true,
			},

			"id": {
				Description: "The string representation of the integer result.",
				Type:        schema.TypeString,
				Computed:    true,
			},
		},
		UseJSONNumber: true,
	}
}

func CreateInteger(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
	var diags diag.Diagnostics
	min := d.Get("min").(int)
	max := d.Get("max").(int)
	seed := d.Get("seed").(string)

	if max < min {
		return append(diags, diag.Diagnostic{
			Severity: diag.Error,
			Summary:  "minimum value needs to be smaller than or equal to maximum value",
		})
	}
	rand := NewRand(seed)
	number := rand.Intn((max+1)-min) + min

	if err := d.Set("result", number); err != nil {
		return diag.Errorf("error setting result: %s", err)
	}

	d.SetId(strconv.Itoa(number))

	return nil
}

func ImportInteger(_ context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
	parts := strings.Split(d.Id(), ",")
	if len(parts) != 3 && len(parts) != 4 {
		return nil, fmt.Errorf("Invalid import usage: expecting {result},{min},{max} or {result},{min},{max},{seed}")
	}

	result, err := strconv.Atoi(parts[0])
	if err != nil {
		return nil, fmt.Errorf("error parsing result: %w", err)
	}

	if err := d.Set("result", result); err != nil {
		return nil, fmt.Errorf("error setting result: %w", err)
	}

	min, err := strconv.Atoi(parts[1])
	if err != nil {
		return nil, fmt.Errorf("error parsing min: %w", err)
	}

	if err := d.Set("min", min); err != nil {
		return nil, fmt.Errorf("error setting min: %w", err)
	}

	max, err := strconv.Atoi(parts[2])
	if err != nil {
		return nil, fmt.Errorf("error parsing max: %w", err)
	}

	if err := d.Set("max", max); err != nil {
		return nil, fmt.Errorf("error setting max: %w", err)
	}

	if len(parts) == 4 {
		if err := d.Set("seed", parts[3]); err != nil {
			return nil, fmt.Errorf("error setting seed: %w", err)
		}
	}

	d.SetId(parts[0])

	return []*schema.ResourceData{d}, nil
}
<|endoftext|>


<commit_before>package morningStar

import (
	"bytes"
	"github.com/ViBiOh/funds/jsonHttp"
	"log"
	"net/http"
	"regexp"
	"sync"
	"time"
)

const refreshDelayInHours = 6
const maxConcurrentFetcher = 32

var requestStatus = regexp.MustCompile(`^/status$`)
var requestList = regexp.MustCompile(`^/list$`)
var requestPerf = regexp.MustCompile(`^/(.+?)$`)

type performance struct {
	ID            string    `json:"id"`
	Isin          string    `json:"isin"`
	Label         string    `json:"label"`
	Category      string    `json:"category"`
	Rating        string    `json:"rating"`
	OneMonth      float64   `json:"1m"`
	ThreeMonths   float64   `json:"3m"`
	SixMonths     float64   `json:"6m"`
	OneYear       float64   `json:"1y"`
	VolThreeYears float64   `json:"v3y"`
	Score         float64   `json:"score"`
	Update        time.Time `json:"ts"`
}

func (perf *performance) computeScore() {
	score := (0.25 * perf.OneMonth) + (0.3 * perf.ThreeMonths) + (0.25 * perf.SixMonths) + (0.2 * perf.OneYear) - (0.1 * perf.VolThreeYears)
	perf.Score = float64(int(score*100)) / 100
}

type results struct {
	Results interface{} `json:"results"`
}

var cacheRequests = make(chan *cacheRequest, maxConcurrentFetcher)

func init() {
	go cacheServer(cacheRequests)
	go func() {
		refreshCache()
		c := time.Tick(refreshDelayInHours * time.Hour)
		for range c {
			refreshCache()
		}
	}()
}

func concurrentRetrievePerformances(ids [][]byte, wg *sync.WaitGroup, performances chan<- *performance, errors chan<- []byte) {
	tokens := make(chan int, maxConcurrentFetcher)

	clearSemaphores := func() {
		wg.Done()
		<-tokens
	}

	for _, id := range ids {
		tokens <- 1

		go func(morningStarID []byte) {
			defer clearSemaphores()
			perf, err := fetchPerformance(morningStarID)
			if err == nil {
				performances <- perf
			} else {
				errors <- morningStarID
			}
		}(id)
	}
}

func retrievePerformances(ids [][]byte) ([]*performance, [][]byte) {
	var wgFetch sync.WaitGroup
	wgFetch.Add(len(ids))

	var wgDrain sync.WaitGroup
	wgDrain.Add(2)

	performancesChan := make(chan *performance, 0)
	errorsChan := make(chan []byte, 0)

	performances := make([]*performance, 0, len(ids))
	errors := make([][]byte, 0)

	go concurrentRetrievePerformances(ids, &wgFetch, performancesChan, errorsChan)

	go func() {
		wgFetch.Wait()
		close(performancesChan)
		close(errorsChan)
	}()

	go func() {
		for perf := range performancesChan {
			performances = append(performances, perf)
		}
		wgDrain.Done()
	}()

	go func() {
		for error := range errorsChan {
			errors = append(errors, error)
		}
		wgDrain.Done()
	}()

	wgDrain.Wait()

	return performances, errors
}

func refreshCache() {
	log.Print(`Cache refresh - start`)
	defer log.Print(`Cache refresh - end`)

	performances, errors := retrievePerformances(morningStarIds)

	if len(errors) > 0 {
		log.Printf(`Errors while refreshing ids %s`, bytes.Join(errors, []byte(`, `)))
	}

	loadCache(cacheRequests, performances)
}

func retrievePerformance(morningStarID []byte) (*performance, error) {
	perf := getCache(cacheRequests, cleanID(morningStarID))
	if perf != nil {
		return perf, nil
	}

	perf, err := fetchPerformance(morningStarID)
	if err != nil {
		return nil, err
	}

	pushCache(cacheRequests, perf)
	morningStarIds = append(morningStarIds, morningStarID)

	return perf, nil
}

func performanceHandler(w http.ResponseWriter, morningStarID []byte) {
	perf, err := retrievePerformance(morningStarID)

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	} else {
		jsonHttp.ResponseJSON(w, *perf)
	}
}

func listPerformances() []*performance {
	performances := make([]*performance, 0, len(morningStarIds))
	for perf := range listCache(cacheRequests) {
		performances = append(performances, perf)
	}

	return performances
}

func listHandler(w http.ResponseWriter, r *http.Request) {
	jsonHttp.ResponseJSON(w, results{listPerformances()})
}

func statusHandler(w http.ResponseWriter, r *http.Request) {
	if len(listPerformances()) > 0 {
		w.Write(`OK`)
	} else {
		w.Write(`KO`)
	}
}

// Handler for MorningStar request. Should be use with net/http
type Handler struct {
}

func (handler Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Header().Add(`Access-Control-Allow-Origin`, `*`)
	w.Header().Add(`Access-Control-Allow-Headers`, `Content-Type`)
	w.Header().Add(`Access-Control-Allow-Methods`, `GET`)
	w.Header().Add(`X-Content-Type-Options`, `nosniff`)

	if r.Method == http.MethodOptions {
		w.Write(nil)
		return
	}

	urlPath := []byte(r.URL.Path)

	if requestList.Match(urlPath) {
		listHandler(w, r)
	} else if requestStatus.Match(urlPath) {
		statusHandler(w, r)
	} else if requestPerf.Match(urlPath) {
		performanceHandler(w, requestPerf.FindSubmatch(urlPath)[1])
	}
}
<commit_msg>Update morningStar.go<commit_after>package morningStar

import (
	"bytes"
	"github.com/ViBiOh/funds/jsonHttp"
	"log"
	"net/http"
	"regexp"
	"sync"
	"time"
)

const refreshDelayInHours = 6
const maxConcurrentFetcher = 32

var requestStatus = regexp.MustCompile(`^/status$`)
var requestList = regexp.MustCompile(`^/list$`)
var requestPerf = regexp.MustCompile(`^/(.+?)$`)

type performance struct {
	ID            string    `json:"id"`
	Isin          string    `json:"isin"`
	Label         string    `json:"label"`
	Category      string    `json:"category"`
	Rating        string    `json:"rating"`
	OneMonth      float64   `json:"1m"`
	ThreeMonths   float64   `json:"3m"`
	SixMonths     float64   `json:"6m"`
	OneYear       float64   `json:"1y"`
	VolThreeYears float64   `json:"v3y"`
	Score         float64   `json:"score"`
	Update        time.Time `json:"ts"`
}

func (perf *performance) computeScore() {
	score := (0.25 * perf.OneMonth) + (0.3 * perf.ThreeMonths) + (0.25 * perf.SixMonths) + (0.2 * perf.OneYear) - (0.1 * perf.VolThreeYears)
	perf.Score = float64(int(score*100)) / 100
}

type results struct {
	Results interface{} `json:"results"`
}

var cacheRequests = make(chan *cacheRequest, maxConcurrentFetcher)

func init() {
	go cacheServer(cacheRequests)
	go func() {
		refreshCache()
		c := time.Tick(refreshDelayInHours * time.Hour)
		for range c {
			refreshCache()
		}
	}()
}

func concurrentRetrievePerformances(ids [][]byte, wg *sync.WaitGroup, performances chan<- *performance, errors chan<- []byte) {
	tokens := make(chan int, maxConcurrentFetcher)

	clearSemaphores := func() {
		wg.Done()
		<-tokens
	}

	for _, id := range ids {
		tokens <- 1

		go func(morningStarID []byte) {
			defer clearSemaphores()
			perf, err := fetchPerformance(morningStarID)
			if err == nil {
				performances <- perf
			} else {
				errors <- morningStarID
			}
		}(id)
	}
}

func retrievePerformances(ids [][]byte) ([]*performance, [][]byte) {
	var wgFetch sync.WaitGroup
	wgFetch.Add(len(ids))

	var wgDrain sync.WaitGroup
	wgDrain.Add(2)

	performancesChan := make(chan *performance, 0)
	errorsChan := make(chan []byte, 0)

	performances := make([]*performance, 0, len(ids))
	errors := make([][]byte, 0)

	go concurrentRetrievePerformances(ids, &wgFetch, performancesChan, errorsChan)

	go func() {
		wgFetch.Wait()
		close(performancesChan)
		close(errorsChan)
	}()

	go func() {
		for perf := range performancesChan {
			performances = append(performances, perf)
		}
		wgDrain.Done()
	}()

	go func() {
		for error := range errorsChan {
			errors = append(errors, error)
		}
		wgDrain.Done()
	}()

	wgDrain.Wait()

	return performances, errors
}

func refreshCache() {
	log.Print(`Cache refresh - start`)
	defer log.Print(`Cache refresh - end`)

	performances, errors := retrievePerformances(morningStarIds)

	if len(errors) > 0 {
		log.Printf(`Errors while refreshing ids %s`, bytes.Join(errors, []byte(`, `)))
	}

	loadCache(cacheRequests, performances)
}

func retrievePerformance(morningStarID []byte) (*performance, error) {
	perf := getCache(cacheRequests, cleanID(morningStarID))
	if perf != nil {
		return perf, nil
	}

	perf, err := fetchPerformance(morningStarID)
	if err != nil {
		return nil, err
	}

	pushCache(cacheRequests, perf)
	morningStarIds = append(morningStarIds, morningStarID)

	return perf, nil
}

func performanceHandler(w http.ResponseWriter, morningStarID []byte) {
	perf, err := retrievePerformance(morningStarID)

	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	} else {
		jsonHttp.ResponseJSON(w, *perf)
	}
}

func listPerformances() []*performance {
	performances := make([]*performance, 0, len(morningStarIds))
	for perf := range listCache(cacheRequests) {
		performances = append(performances, perf)
	}

	return performances
}

func listHandler(w http.ResponseWriter, r *http.Request) {
	jsonHttp.ResponseJSON(w, results{listPerformances()})
}

func statusHandler(w http.ResponseWriter, r *http.Request) {
	if len(listPerformances()) > 0 {
		w.Write([]byte(`OK`))
	} else {
		w.Write([]byte(`KO`))
	}
}

// Handler for MorningStar request. Should be use with net/http
type Handler struct {
}

func (handler Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Header().Add(`Access-Control-Allow-Origin`, `*`)
	w.Header().Add(`Access-Control-Allow-Headers`, `Content-Type`)
	w.Header().Add(`Access-Control-Allow-Methods`, `GET`)
	w.Header().Add(`X-Content-Type-Options`, `nosniff`)

	if r.Method == http.MethodOptions {
		w.Write(nil)
		return
	}

	urlPath := []byte(r.URL.Path)

	if requestList.Match(urlPath) {
		listHandler(w, r)
	} else if requestStatus.Match(urlPath) {
		statusHandler(w, r)
	} else if requestPerf.Match(urlPath) {
		performanceHandler(w, requestPerf.FindSubmatch(urlPath)[1])
	}
}
<|endoftext|>


<commit_before>package morphemekor
<commit_msg>Update Segmentation<commit_after>package morphemekor

import (
	"bytes"
	stdlog "log"
	"os"
	"strings"
)

// func main() {
// 	// bs := []byte{}
// 	// b := New().Add("morphemekor")
// 	// for {
// 	// 	n, err := b.ReadByte()
// 	// 	if err != nil {
// 	// 		log.Println(err)
// 	// 		break
// 	// 	}
// 	// 	bs = append(bs, n)
// 	// }
// 	// fmt.Println(bs)
// 	// fmt.Println(string(bs))
// 	fmt.Println(Segment("나는친구한테듣는다안녕"))
// }

// Stream is a stream of bytes.
type Stream struct {
	*bytes.Buffer
}

// New returns *morphemekor.Stream.
func New() *Stream {
	out := new(Stream)
	out.Buffer = new(bytes.Buffer)
	return out
}

// Init initializes Stream.
func (s *Stream) Init() *Stream {
	// *s = *New()
	s.Buffer.Reset()
	return s
}

// Add adds a string to the Stream.
func (s *Stream) Add(str string) *Stream {
	s.Buffer.WriteString(str)
	return s
}

// Get returns the string from the String.
func (s *Stream) Get() string {
	return s.Buffer.String()
}

var log *stdlog.Logger

func init() {
	// import stdlog "log"
	log = stdlog.New(
		os.Stdout,
		"[Log] ",
		stdlog.Ldate|stdlog.Ltime|stdlog.Lshortfile,
	)
}

// Segment segments Korean with morphemic approach.
func Segment(str string) string {
	if len(str)/3 < 4 {
		return str
	}
	isInflection1 := make(map[string]bool)
	for _, elem := range strings.Split("이히리기었렀", "") {
		isInflection1[elem] = true
	}
	isTargetEnding1 := make(map[string]bool)
	for _, elem := range strings.Split("다,고,지,마,지만", ",") {
		isTargetEnding1[elem] = true
	}
	isTargetEnding2 := make(map[string]bool)
	for _, elem := range strings.Split("지만", ",") {
		isTargetEnding2[elem] = true
	}
	isPostposition1 := make(map[string]bool)
	for _, elem := range strings.Split("은,이,가,도,을,를,께,로,의,와,과", ",") { // 는,
		isPostposition1[elem] = true
	}
	isPostposition2 := make(map[string]bool)
	for _, elem := range strings.Split("에,한,으", ",") {
		isPostposition2[elem] = true
	}
	isPostposition3 := make(map[string]bool)
	for _, elem := range strings.Split("에게,한테,으로,에서", ",") {
		isPostposition3[elem] = true
	}

	buffer := New()
	wasInflection := false
	doSkip := false
	characters := strings.Split(str, "")

	for idx, elem := range characters {
		if doSkip {
			doSkip = false
			continue
		}
		if idx < 2 {
			buffer.Add(elem)
			doSkip = false
			continue
		}
		if elem == " " {
			buffer.Add(elem)
			doSkip = false
			continue
		}
		if _, ok := isInflection1[elem]; ok {
			wasInflection = true
			doSkip = false
			buffer.Add(elem)
			continue
		}
		if wasInflection {
			if _, ok := isTargetEnding1[elem]; ok {
				buffer.Add(elem)
				log.Println(elem)
				buffer.Add(" ")
				wasInflection = false
				doSkip = false
				continue
			} else {
				if len(characters) > idx+1 {
					if _, ok := isTargetEnding2[elem]; ok {
						buffer.Add(elem)
						log.Println(elem)
						buffer.Add(" ")
						wasInflection = false
						doSkip = false
						continue
					}
				}
			}
		}
		if _, ok := isPostposition1[elem]; ok {
			buffer.Add(elem)
			buffer.Add(" ")
			wasInflection = false
			continue
		} else if _, ok := isPostposition2[elem]; ok {
			if len(characters) > idx+1 {
				char := characters[idx] + characters[idx+1]
				if _, ok := isPostposition3[char]; ok {
					buffer.Add(char)
					log.Println(char)
					buffer.Add(" ")
					wasInflection = false
					doSkip = true
					continue
				}
			}
		}
		wasInflection = false
		doSkip = false
		buffer.Add(elem)
	}
	return buffer.Get()
}
<|endoftext|>


<commit_before>package process

import (
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/resin-io/edge-node-manager/application"
	"github.com/resin-io/edge-node-manager/config"
	deviceStatus "github.com/resin-io/edge-node-manager/device/status"
	processStatus "github.com/resin-io/edge-node-manager/process/status"
	"github.com/resin-io/edge-node-manager/radio/bluetooth"
)

var (
	delay          time.Duration
	CurrentStatus  processStatus.Status
	TargetStatus   processStatus.Status
	UpdatesPending bool
)

// Run processes the application, checking for new commits, provisioning and updating devices
func Run(a *application.Application) []error {
	log.Info("----------------------------------------------------------------------------------------------------")

	// Pause the process if necessary
	if err := pause(); err != nil {
		return []error{err}
	}

	// Validate application to ensure the board type has been set
	if a.BoardType == "" {
		log.WithFields(log.Fields{
			"Application": a.Name,
			"Error":       "Application board type not set",
		}).Warn("Processing application")
		return nil
	}

	// Handle delete flag
	if err := a.HandleDeleteFlag(); err != nil {
		return []error{err}
	}

	// Print application info
	log.WithFields(log.Fields{
		"Application":       a.Name,
		"Number of devices": len(a.Devices),
	}).Info("Processing application")

	// Reset the bluetooth device to clean up any left over go routines etc. Quick fix
	if err := bluetooth.ResetDevice(); err != nil {
		return []error{err}
	}

	// Get all online devices associated with this application
	if err := a.GetOnlineDevices(); err != nil {
		return []error{err}
	}

	// Provision non-provisoned online devices associated with this application
	if errs := a.ProvisionDevices(); errs != nil {
		return errs
	}

	// Set the status of all offline provisioned devices associated with this application to OFFLINE
	if errs := a.SetOfflineDeviceStatus(); errs != nil {
		return errs
	}

	// Update firmware for all online devices associated with this application
	if errs := a.UpdateOnlineDevices(); errs != nil {
		return errs
	}

	// Update config for all online devices associated with this application
	if errs := a.UpdateConfigOnlineDevices(); errs != nil {
		return errs
	}

	// Update environment for all online devices associated with this application
	if errs := a.UpdateEnvironmentOnlineDevices(); errs != nil {
		return errs
	}

	// Handle device flags
	if err := a.HandleFlags(); err != nil {
		return []error{err}
	}

	// Put all provisioned devices associated with this application
	if err := a.PutDevices(); err != nil {
		return []error{err}
	}

	return nil
}

func Pending() {
	for _, a := range application.List {
		for _, d := range a.Devices {
			if d.Commit != d.TargetCommit && d.Status != deviceStatus.OFFLINE {
				UpdatesPending = true
				return
			}
		}
	}

	UpdatesPending = false
	return
}

func init() {
	log.SetLevel(config.GetLogLevel())

	var err error
	if delay, err = config.GetPauseDelay(); err != nil {
		log.WithFields(log.Fields{
			"Error": err,
		}).Fatal("Unable to load pause delay")
	}

	CurrentStatus = processStatus.RUNNING
	TargetStatus = processStatus.RUNNING
	UpdatesPending = false

	log.WithFields(log.Fields{
		"Pause delay": delay,
	}).Debug("Initialise process")
}

func pause() error {
	if TargetStatus != processStatus.PAUSED {
		return nil
	}

	if err := bluetooth.CloseDevice(); err != nil {
		return err
	}

	CurrentStatus = processStatus.PAUSED
	log.WithFields(log.Fields{
		"Status": CurrentStatus,
	}).Info("Process status")

	for TargetStatus == processStatus.PAUSED {
		time.Sleep(delay * time.Second)
	}

	if err := bluetooth.OpenDevice(); err != nil {
		return err
	}

	CurrentStatus = processStatus.RUNNING
	log.WithFields(log.Fields{
		"Status": CurrentStatus,
	}).Info("Process status")

	return nil
}
<commit_msg>Defer save state<commit_after>package process

import (
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/resin-io/edge-node-manager/application"
	"github.com/resin-io/edge-node-manager/config"
	deviceStatus "github.com/resin-io/edge-node-manager/device/status"
	processStatus "github.com/resin-io/edge-node-manager/process/status"
	"github.com/resin-io/edge-node-manager/radio/bluetooth"
)

var (
	delay          time.Duration
	CurrentStatus  processStatus.Status
	TargetStatus   processStatus.Status
	UpdatesPending bool
)

// Run processes the application, checking for new commits, provisioning and updating devices
func Run(a *application.Application) []error {
	log.Info("----------------------------------------------------------------------------------------------------")

	// Put all provisioned devices associated with this application
	defer a.PutDevices()

	// Pause the process if necessary
	if err := pause(); err != nil {
		return []error{err}
	}

	// Validate application to ensure the board type has been set
	if a.BoardType == "" {
		log.WithFields(log.Fields{
			"Application": a.Name,
			"Error":       "Application board type not set",
		}).Warn("Processing application")
		return nil
	}

	// Handle delete flag
	if err := a.HandleDeleteFlag(); err != nil {
		return []error{err}
	}

	// Print application info
	log.WithFields(log.Fields{
		"Application":       a.Name,
		"Number of devices": len(a.Devices),
	}).Info("Processing application")

	// Reset the bluetooth device to clean up any left over go routines etc. Quick fix
	if err := bluetooth.ResetDevice(); err != nil {
		return []error{err}
	}

	// Get all online devices associated with this application
	if err := a.GetOnlineDevices(); err != nil {
		return []error{err}
	}

	// Provision non-provisoned online devices associated with this application
	if errs := a.ProvisionDevices(); errs != nil {
		return errs
	}

	// Set the status of all offline provisioned devices associated with this application to OFFLINE
	if errs := a.SetOfflineDeviceStatus(); errs != nil {
		return errs
	}

	// Update firmware for all online devices associated with this application
	if errs := a.UpdateOnlineDevices(); errs != nil {
		return errs
	}

	// Update config for all online devices associated with this application
	if errs := a.UpdateConfigOnlineDevices(); errs != nil {
		return errs
	}

	// Update environment for all online devices associated with this application
	if errs := a.UpdateEnvironmentOnlineDevices(); errs != nil {
		return errs
	}

	// Handle device flags
	if err := a.HandleFlags(); err != nil {
		return []error{err}
	}

	return nil
}

func Pending() {
	for _, a := range application.List {
		for _, d := range a.Devices {
			if d.Commit != d.TargetCommit && d.Status != deviceStatus.OFFLINE {
				UpdatesPending = true
				return
			}
		}
	}

	UpdatesPending = false
	return
}

func init() {
	log.SetLevel(config.GetLogLevel())

	var err error
	if delay, err = config.GetPauseDelay(); err != nil {
		log.WithFields(log.Fields{
			"Error": err,
		}).Fatal("Unable to load pause delay")
	}

	CurrentStatus = processStatus.RUNNING
	TargetStatus = processStatus.RUNNING
	UpdatesPending = false

	log.WithFields(log.Fields{
		"Pause delay": delay,
	}).Debug("Initialise process")
}

func pause() error {
	if TargetStatus != processStatus.PAUSED {
		return nil
	}

	if err := bluetooth.CloseDevice(); err != nil {
		return err
	}

	CurrentStatus = processStatus.PAUSED
	log.WithFields(log.Fields{
		"Status": CurrentStatus,
	}).Info("Process status")

	for TargetStatus == processStatus.PAUSED {
		time.Sleep(delay * time.Second)
	}

	if err := bluetooth.OpenDevice(); err != nil {
		return err
	}

	CurrentStatus = processStatus.RUNNING
	log.WithFields(log.Fields{
		"Status": CurrentStatus,
	}).Info("Process status")

	return nil
}
<|endoftext|>


<commit_before>package process

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"sync"
	"syscall"
	"time"
)

// BufferSize determines how much output will be read into memory before resorting to using a temporary file
const BufferSize = 1048576

// UnknownExit is used when the return/exit-code of the command is not known.
const UnknownExit = 1

func getShell() string {
	shell := os.Getenv("SHELL")
	if shell == "" {
		shell = "sh"
	}
	return shell
}

// Command contains a buffered reader with the realized stdout of the process along with the exit code.
type Command struct {
	*bufio.Reader
	tmp      *os.File
	Err      error
	cmd      string
	Duration time.Duration
}

func (c *Command) error() string {
	if c == nil || c.Err == nil {
		return ""
	}
	return c.Err.Error()
}

// Close the temp file associated with the command
func (c *Command) Close() error {
	if c.tmp == nil {
		return nil
	}
	return c.tmp.Close()
}

// Cleanup makes sure the tempfile is closed an deleted.
func (c *Command) Cleanup() {
	if c.tmp != nil {
		c.Close()
		cleanup(c)
	}
}

// String returns a representation of the command that includes run-time, error (if any) and the first 20 chars of stdout.
func (c *Command) String() string {
	cmd := c.cmd
	if len(c.cmd) > 100 {
		cmd = cmd[:80] + "..."
	}
	out, _ := c.Peek(20)
	prompt := ", stdout[:20]: "
	if len(out) < 20 {
		prompt = "stdout: "
	}
	prompt += fmt.Sprintf("'%s'", strings.Replace(string(out), "\n", "\\n", -1))
	errString := ""
	if e := c.error(); e != "" {
		errString = fmt.Sprintf(", error: %s", e)
	}
	exString := ""
	if ex := c.ExitCode(); ex != 0 {
		exString = fmt.Sprintf(", exit-code: %d", ex)
	}

	return fmt.Sprintf("Command('%s', %s%s%s, run-time: %s)",
		cmd, prompt, exString, errString, c.Duration)
}

// ExitCode returns the exit code associated with a given error
func (c *Command) ExitCode() int {
	if c.Err == nil {
		return 0
	}
	if ex, ok := c.Err.(*exec.ExitError); ok {
		if st, ok := ex.Sys().(syscall.WaitStatus); ok {
			return st.ExitStatus()
		}
	}
	return UnknownExit
}

func cleanup(c *Command) {
	c.tmp.Close()
	os.Remove(c.tmp.Name())
}

func newCommand(rdr *bufio.Reader, tmp *os.File, cmd string, err error) *Command {
	c := &Command{rdr, tmp, err, cmd, 0}
	if tmp != nil {
		runtime.SetFinalizer(c, cleanup)
	}
	return c
}

var prefix = fmt.Sprintf("gargs.%d.", os.Getpid())

// Run takes a command string, executes the command,
// Blocks until the output is finished and returns a *Command
// that is an io.Reader. If retries > 0 it will retry on a
// non-zero exit-code.
func Run(command string, retries int) *Command {
	t := time.Now()
	c := oneRun(command)
	for retries > 0 && c.ExitCode() != 0 {
		retries--
		c = oneRun(command)
	}
	c.Duration = time.Since(t)
	return c
}

func oneRun(command string) *Command {

	cmd := exec.Command(getShell(), "-c", command)

	opipe, err := cmd.StdoutPipe()
	if err != nil {
		return newCommand(nil, nil, command, err)
	}
	defer opipe.Close()

	cmd.Stderr = os.Stderr

	err = cmd.Start()
	if err != nil {
		return newCommand(nil, nil, command, err)
	}

	bpipe := bufio.NewReaderSize(opipe, BufferSize)

	var res []byte
	res, err = bpipe.Peek(BufferSize)

	// less than BufferSize bytes in output...
	if err == bufio.ErrBufferFull || err == io.EOF {
		err = cmd.Wait()
		return newCommand(bufio.NewReader(bytes.NewReader(res)), nil, command, err)
	}
	if err != nil {
		return newCommand(nil, nil, command, err)
	}

	// more than BufferSize bytes in output. must use tmpfile
	var tmp *os.File
	tmp, err = ioutil.TempFile("", prefix)
	if err != nil {
		return newCommand(bufio.NewReader(bytes.NewReader(res)), tmp, command, err)
	}
	btmp := bufio.NewWriter(tmp)
	_, err = io.CopyBuffer(btmp, bpipe, res)
	if err != nil {
		return newCommand(bufio.NewReader(bytes.NewReader(res)), tmp, command, err)
	}
	opipe.Close()
	btmp.Flush()
	_, err = tmp.Seek(0, 0)
	if err == nil {
		err = cmd.Wait()
	}
	return newCommand(bufio.NewReader(tmp), tmp, command, err)
}

// Runner accepts commands from a channel and sends a bufio.Reader on the returned channel.
// done allows the caller to stop Runner, for example if an error occurs.
// It will parallelize according to GOMAXPROCS.
func Runner(commands <-chan string, retries int, cancel <-chan bool) chan *Command {

	stdout := make(chan *Command, runtime.GOMAXPROCS(0))

	wg := &sync.WaitGroup{}
	wg.Add(runtime.GOMAXPROCS(0))

	// Start a number of workers equal to the requested procs.
	for i := 0; i < runtime.GOMAXPROCS(0); i++ {
		go func() {
			defer wg.Done()
			// workers read off the same channel of incoming commands.
			for cmdStr := range commands {
				select {
				case stdout <- Run(cmdStr, retries):
				// if we receive from this, we must exit.
				// receive from closed channel will continually yield false
				// so it does what we expect.
				case <-cancel:
					close(stdout)
					return
				}
			}
		}()
	}

	// wait for all the workers to finish.
	go func() {
		wg.Wait()
		close(stdout)
	}()

	return stdout
}
<commit_msg>re-ordering<commit_after>package process

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"sync"
	"syscall"
	"time"
)

// BufferSize determines how much output will be read into memory before resorting to using a temporary file
const BufferSize = 1048576

// UnknownExit is used when the return/exit-code of the command is not known.
const UnknownExit = 1

// prefix for tmp files.
var prefix = fmt.Sprintf("gargs.%d.", os.Getpid())

func getShell() string {
	shell := os.Getenv("SHELL")
	if shell == "" {
		shell = "sh"
	}
	return shell
}

// Command contains a buffered reader with the realized stdout of the process along with the exit code.
type Command struct {
	*bufio.Reader
	tmp      *os.File
	Err      error
	cmd      string
	Duration time.Duration
}

func (c *Command) error() string {
	if c == nil || c.Err == nil {
		return ""
	}
	return c.Err.Error()
}

// Close the temp file associated with the command
func (c *Command) Close() error {
	if c.tmp == nil {
		return nil
	}
	return c.tmp.Close()
}

// String returns a representation of the command that includes run-time, error (if any) and the first 20 chars of stdout.
func (c *Command) String() string {
	cmd := c.cmd
	if len(c.cmd) > 100 {
		cmd = cmd[:80] + "..."
	}
	out, _ := c.Peek(20)
	prompt := ", stdout[:20]: "
	if len(out) < 20 {
		prompt = "stdout: "
	}
	prompt += fmt.Sprintf("'%s'", strings.Replace(string(out), "\n", "\\n", -1))
	errString := ""
	if e := c.error(); e != "" {
		errString = fmt.Sprintf(", error: %s", e)
	}
	exString := ""
	if ex := c.ExitCode(); ex != 0 {
		exString = fmt.Sprintf(", exit-code: %d", ex)
	}

	return fmt.Sprintf("Command('%s', %s%s%s, run-time: %s)",
		cmd, prompt, exString, errString, c.Duration)
}

// ExitCode returns the exit code associated with a given error
func (c *Command) ExitCode() int {
	if c.Err == nil {
		return 0
	}
	if ex, ok := c.Err.(*exec.ExitError); ok {
		if st, ok := ex.Sys().(syscall.WaitStatus); ok {
			return st.ExitStatus()
		}
	}
	return UnknownExit
}

// Cleanup makes sure the tempfile is closed an deleted.
func (c *Command) Cleanup() {
	if c.tmp != nil {
		c.Close()
		cleanup(c)
	}
}

func cleanup(c *Command) {
	c.tmp.Close()
	os.Remove(c.tmp.Name())
}

func newCommand(rdr *bufio.Reader, tmp *os.File, cmd string, err error) *Command {
	c := &Command{rdr, tmp, err, cmd, 0}
	if tmp != nil {
		runtime.SetFinalizer(c, cleanup)
	}
	return c
}

// Run takes a command string, executes the command,
// Blocks until the output is finished and returns a *Command
// that is an io.Reader. If retries > 0 it will retry on a
// non-zero exit-code.
func Run(command string, retries int) *Command {
	t := time.Now()
	c := oneRun(command)
	for retries > 0 && c.ExitCode() != 0 {
		retries--
		c = oneRun(command)
	}
	c.Duration = time.Since(t)
	return c
}

func oneRun(command string) *Command {

	cmd := exec.Command(getShell(), "-c", command)

	opipe, err := cmd.StdoutPipe()
	if err != nil {
		return newCommand(nil, nil, command, err)
	}
	defer opipe.Close()

	cmd.Stderr = os.Stderr

	err = cmd.Start()
	if err != nil {
		return newCommand(nil, nil, command, err)
	}

	bpipe := bufio.NewReaderSize(opipe, BufferSize)

	var res []byte
	res, err = bpipe.Peek(BufferSize)

	// less than BufferSize bytes in output...
	if err == bufio.ErrBufferFull || err == io.EOF {
		err = cmd.Wait()
		return newCommand(bufio.NewReader(bytes.NewReader(res)), nil, command, err)
	}
	if err != nil {
		return newCommand(nil, nil, command, err)
	}

	// more than BufferSize bytes in output. must use tmpfile
	var tmp *os.File
	tmp, err = ioutil.TempFile("", prefix)
	if err != nil {
		return newCommand(bufio.NewReader(bytes.NewReader(res)), tmp, command, err)
	}
	btmp := bufio.NewWriter(tmp)
	_, err = io.CopyBuffer(btmp, bpipe, res)
	if err != nil {
		return newCommand(bufio.NewReader(bytes.NewReader(res)), tmp, command, err)
	}
	opipe.Close()
	btmp.Flush()
	_, err = tmp.Seek(0, 0)
	if err == nil {
		err = cmd.Wait()
	}
	return newCommand(bufio.NewReader(tmp), tmp, command, err)
}

// Runner accepts commands from a channel and sends a bufio.Reader on the returned channel.
// done allows the caller to stop Runner, for example if an error occurs.
// It will parallelize according to GOMAXPROCS.
func Runner(commands <-chan string, retries int, cancel <-chan bool) chan *Command {

	stdout := make(chan *Command, runtime.GOMAXPROCS(0))

	wg := &sync.WaitGroup{}
	wg.Add(runtime.GOMAXPROCS(0))

	// Start a number of workers equal to the requested procs.
	for i := 0; i < runtime.GOMAXPROCS(0); i++ {
		go func() {
			defer wg.Done()
			// workers read off the same channel of incoming commands.
			for cmdStr := range commands {
				select {
				case stdout <- Run(cmdStr, retries):
				// if we receive from this, we must exit.
				// receive from closed channel will continually yield false
				// so it does what we expect.
				case <-cancel:
					close(stdout)
					return
				}
			}
		}()
	}

	// wait for all the workers to finish.
	go func() {
		wg.Wait()
		close(stdout)
	}()

	return stdout
}
<|endoftext|>


<commit_before>package gopenid

import (
	"crypto/hmac"
	"crypto/sha1"
	"crypto/sha256"
	"errors"
	"fmt"
	"github.com/nu7hatch/gouuid"
	"hash"
	"io"
	"strings"
	"time"
)

const (
	ASSOCIATION_LIFETIME = 60 * 60 * 24 * 1
)

var (
	ErrGeneratingAssociationFailed = errors.New("generating association failed")
	ErrAssociationNotFound         = errors.New("association not found")
	ErrUnknownSessionType          = errors.New("unknown session type")
	ErrUnknownAssocType            = errors.New("unknown association type")

	ASSOC_HMAC_SHA1 = AssocType{
		name:       "HMAC-SHA1",
		hashFunc:   sha1.New,
		secretSize: sha1.Size,
	}
	ASSOC_HMAC_SHA256 = AssocType{
		name:       "HMAC-SHA256",
		hashFunc:   sha256.New,
		secretSize: sha256.Size,
	}

	SESSION_DH_SHA1 = SessionType{
		name: "DH-SHA1",
		assocTypes: []AssocType{
			ASSOC_HMAC_SHA1,
		},
	}
	SESSION_DH_SHA256 = SessionType{
		name: "DH-SHA256",
		assocTypes: []AssocType{
			ASSOC_HMAC_SHA256,
		},
	}
	SESSION_NO_ENCRYPTION = SessionType{
		name: "no-encryption",
		assocTypes: []AssocType{
			ASSOC_HMAC_SHA1,
			ASSOC_HMAC_SHA256,
		},
	}
)

type AssocType struct {
	name       string
	hashFunc   func() hash.Hash
	secretSize int
}

func (t *AssocType) Name() string {
	return t.name
}

func (t *AssocType) GetSecretSize() int {
	return t.secretSize
}

func GetAssocTypeByName(name string) (assocType AssocType, err error) {
	switch name {
	case "HMAC-SHA1":
		assocType = ASSOC_HMAC_SHA1
	case "HMAC-SHA256":
		assocType = ASSOC_HMAC_SHA256
	default:
		err = ErrUnknownAssocType
	}

	return
}

type SessionType struct {
	name       string
	assocTypes []AssocType
}

func (t *SessionType) Name() string {
	return t.name
}

func GetSessionTypeByName(name string) (sessionType SessionType, err error) {
	switch name {
	case "no-encryption":
		sessionType = SESSION_NO_ENCRYPTION
	case "DH-SHA1":
		sessionType = SESSION_DH_SHA1
	case "DH-SHA256":
		sessionType = SESSION_DH_SHA256
	default:
		err = ErrUnknownSessionType
	}

	return
}

type Association struct {
	assocType   AssocType
	handle      string
	secret      []byte
	expires     int64
	isStateless bool
}

func NewAssociation(assocType AssocType, handle string, secret []byte, expires int64, isStateless bool) *Association {
	if expires < 1 {
		expires = time.Now().Unix() + ASSOCIATION_LIFETIME
	}

	return &Association{
		assocType:   assocType,
		handle:      handle,
		secret:      secret,
		expires:     expires,
		isStateless: isStateless,
	}
}

func CreateAssociation(random io.Reader, assocType AssocType, expires int64, isStateless bool) (assoc *Association, err error) {
	handle, err := uuid.NewV4()
	if err != nil {
		err = ErrGeneratingAssociationFailed
		return
	}

	secret := make([]byte, assocType.GetSecretSize())
	_, err = io.ReadFull(random, secret)
	if err != nil {
		err = ErrGeneratingAssociationFailed
		return
	}

	assoc = NewAssociation(assocType, handle.String(), secret, expires, isStateless)
	return
}

func (assoc *Association) GetAssocType() AssocType {
	return assoc.assocType
}

func (assoc *Association) GetHandle() string {
	return assoc.handle
}

func (assoc *Association) GetSecret() []byte {
	return assoc.secret
}

func (assoc *Association) GetExpires() int64 {
	return assoc.expires
}

func (assoc *Association) IsValid() bool {
	return time.Now().Before(time.Unix(assoc.GetExpires(), 0))
}

func (assoc *Association) IsStateless() bool {
	return assoc.isStateless
}

func (assoc *Association) Sign(msg Message, signed []string) (err error) {
	order := make([]string, len(signed))
	for i, key := range signed {
		order[i] = fmt.Sprintf("openid.%s", key)
	}

	mac := hmac.New(assoc.assocType.hashFunc, assoc.secret)
	kv, err := msg.ToKeyValue(order)
	if err != nil {
		return
	}
	mac.Write(kv)
	sig, err := EncodeBase64(mac.Sum(nil))
	if err != nil {
		return
	}

	msg.AddArg(
		NewMessageKey(msg.GetOpenIDNamespace(), "signed"),
		MessageValue(strings.Join(signed, ",")),
	)
	msg.AddArg(
		NewMessageKey(msg.GetOpenIDNamespace(), "sig"),
		MessageValue(sig),
	)

	return
}
<commit_msg>define ASSOC_DEFAULT, SESSION_DEFAULT<commit_after>package gopenid

import (
	"crypto/hmac"
	"crypto/sha1"
	"crypto/sha256"
	"errors"
	"fmt"
	"github.com/nu7hatch/gouuid"
	"hash"
	"io"
	"strings"
	"time"
)

const (
	ASSOCIATION_LIFETIME = 60 * 60 * 24 * 1
)

var (
	ErrGeneratingAssociationFailed = errors.New("generating association failed")
	ErrAssociationNotFound         = errors.New("association not found")
	ErrUnknownSessionType          = errors.New("unknown session type")
	ErrUnknownAssocType            = errors.New("unknown association type")

	ASSOC_HMAC_SHA1 = AssocType{
		name:       "HMAC-SHA1",
		hashFunc:   sha1.New,
		secretSize: sha1.Size,
	}
	ASSOC_HMAC_SHA256 = AssocType{
		name:       "HMAC-SHA256",
		hashFunc:   sha256.New,
		secretSize: sha256.Size,
	}

	SESSION_DH_SHA1 = SessionType{
		name: "DH-SHA1",
		assocTypes: []AssocType{
			ASSOC_HMAC_SHA1,
		},
	}
	SESSION_DH_SHA256 = SessionType{
		name: "DH-SHA256",
		assocTypes: []AssocType{
			ASSOC_HMAC_SHA256,
		},
	}
	SESSION_NO_ENCRYPTION = SessionType{
		name: "no-encryption",
		assocTypes: []AssocType{
			ASSOC_HMAC_SHA1,
			ASSOC_HMAC_SHA256,
		},
	}

	SESSION_DEFAULT = SESSION_DH_SHA256
	ASSOC_DEFAULT   = ASSOC_HMAC_SHA256
)

type AssocType struct {
	name       string
	hashFunc   func() hash.Hash
	secretSize int
}

func (t *AssocType) Name() string {
	return t.name
}

func (t *AssocType) GetSecretSize() int {
	return t.secretSize
}

func GetAssocTypeByName(name string) (assocType AssocType, err error) {
	switch name {
	case "HMAC-SHA1":
		assocType = ASSOC_HMAC_SHA1
	case "HMAC-SHA256":
		assocType = ASSOC_HMAC_SHA256
	default:
		err = ErrUnknownAssocType
	}

	return
}

type SessionType struct {
	name       string
	assocTypes []AssocType
}

func (t *SessionType) Name() string {
	return t.name
}

func GetSessionTypeByName(name string) (sessionType SessionType, err error) {
	switch name {
	case "no-encryption":
		sessionType = SESSION_NO_ENCRYPTION
	case "DH-SHA1":
		sessionType = SESSION_DH_SHA1
	case "DH-SHA256":
		sessionType = SESSION_DH_SHA256
	default:
		err = ErrUnknownSessionType
	}

	return
}

type Association struct {
	assocType   AssocType
	handle      string
	secret      []byte
	expires     int64
	isStateless bool
}

func NewAssociation(assocType AssocType, handle string, secret []byte, expires int64, isStateless bool) *Association {
	if expires < 1 {
		expires = time.Now().Unix() + ASSOCIATION_LIFETIME
	}

	return &Association{
		assocType:   assocType,
		handle:      handle,
		secret:      secret,
		expires:     expires,
		isStateless: isStateless,
	}
}

func CreateAssociation(random io.Reader, assocType AssocType, expires int64, isStateless bool) (assoc *Association, err error) {
	handle, err := uuid.NewV4()
	if err != nil {
		err = ErrGeneratingAssociationFailed
		return
	}

	secret := make([]byte, assocType.GetSecretSize())
	_, err = io.ReadFull(random, secret)
	if err != nil {
		err = ErrGeneratingAssociationFailed
		return
	}

	assoc = NewAssociation(assocType, handle.String(), secret, expires, isStateless)
	return
}

func (assoc *Association) GetAssocType() AssocType {
	return assoc.assocType
}

func (assoc *Association) GetHandle() string {
	return assoc.handle
}

func (assoc *Association) GetSecret() []byte {
	return assoc.secret
}

func (assoc *Association) GetExpires() int64 {
	return assoc.expires
}

func (assoc *Association) IsValid() bool {
	return time.Now().Before(time.Unix(assoc.GetExpires(), 0))
}

func (assoc *Association) IsStateless() bool {
	return assoc.isStateless
}

func (assoc *Association) Sign(msg Message, signed []string) (err error) {
	order := make([]string, len(signed))
	for i, key := range signed {
		order[i] = fmt.Sprintf("openid.%s", key)
	}

	mac := hmac.New(assoc.assocType.hashFunc, assoc.secret)
	kv, err := msg.ToKeyValue(order)
	if err != nil {
		return
	}
	mac.Write(kv)
	sig, err := EncodeBase64(mac.Sum(nil))
	if err != nil {
		return
	}

	msg.AddArg(
		NewMessageKey(msg.GetOpenIDNamespace(), "signed"),
		MessageValue(strings.Join(signed, ",")),
	)
	msg.AddArg(
		NewMessageKey(msg.GetOpenIDNamespace(), "sig"),
		MessageValue(sig),
	)

	return
}
<|endoftext|>