<commit_before>// Copyright 2015 Google Inc. All rights reserved.
//
// 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 storage provides the abstraction to build drivers for BadWolf.
package storage

import (
	"bytes"
	"strconv"
	"time"

	"github.com/google/badwolf/triple"
	"github.com/google/badwolf/triple/node"
	"github.com/google/badwolf/triple/predicate"
	"golang.org/x/net/context"
)

// LookupOptions allows to specify the behavior of the lookup operations.
type LookupOptions struct {
	// MaxElements list the maximum number of elements to return. If not
	// set it returns all the lookup results.
	MaxElements int

	// LowerAnchor, if provided, represents the lower time anchor to be considered.
	LowerAnchor *time.Time

	// UpperAnchor, if provided, represents the upper time anchor to be considered.
	UpperAnchor *time.Time
}

// String returns a readable version of the LookupOptions instance.
func (l *LookupOptions) String() string {
	b := bytes.NewBufferString("<limit=")
	b.WriteString(strconv.Itoa(l.MaxElements))
	b.WriteString(", lower_anchor=")
	if l.LowerAnchor != nil {
		b.WriteString(l.LowerAnchor.String())
	} else {
		b.WriteString("nil")
	}
	b.WriteString(", upper_anchor=")
	if l.UpperAnchor != nil {
		b.WriteString(l.UpperAnchor.String())
	} else {
		b.WriteString("nil")
	}
	b.WriteString(">")
	return b.String()
}

// DefaultLookup provides the default lookup behavior.
var DefaultLookup = &LookupOptions{}

// Store interface describes the low lever API that allows to create new graphs.
type Store interface {
	// Name returns the ID of the backend being used.
	Name(ctx context.Context) string

	// Version returns the version of the driver implementation.
	Version(ctx context.Context) string

	// NewGraph creates a new graph. Creating an already existing graph
	// should return an error.
	NewGraph(ctx context.Context, id string) (Graph, error)

	// Graph returns an existing graph if available. Getting a non existing
	// graph should return an error.
	Graph(ctx context.Context, id string) (Graph, error)

	// DeleteGraph deletes an existing graph. Deleting a non existing graph
	// should return an error.
	DeleteGraph(ctx context.Context, id string) error

	// GraphNames returns the current available graph names in the store.
	GraphNames(ctx context.Context, names chan<- string) error
}

// Graph interface describes the low level API that storage drivers need
// to implement to provide a compliant graph storage that can be used with
// BadWolf.
//
// If you are implementing a driver or just using a low lever driver directly
// it is important for you to keep in mind that you will need to drain the
// provided channel. Otherwise you run the risk of leaking go routines.
type Graph interface {
	// ID returns the id for this graph.
	ID(ctx context.Context) string

	// AddTriples adds the triples to the storage. Adding a triple that already
	// exists should not fail.
	AddTriples(ctx context.Context, ts []*triple.Triple) error

	// RemoveTriples removes the triples from the storage. Removing triples that
	// are not present on the store should not fail.
	RemoveTriples(ctx context.Context, ts []*triple.Triple) error

	// Objects pushes to the provided channel the objects for the given object and
	// predicate. The function does not return immediately but spawns a goroutine
	// to satisfy elements in the channel.
	//
	// Given a subject and a predicate, this method retrieves the objects of
	// triples that match them. By default, if does not limit the maximum number
	// of possible objects returned, unless properly specified by provided lookup
	// options.
	//
	// If the provided predicate is immutable it will return all the possible
	// subject values or the number of max elements specified. There is no
	// requirement on how to sample the returned max elements.
	//
	// If the predicate is an unanchored temporal triple and no time anchors are
	// provided in the lookup options, it will return all the available objects.
	// If time anchors are provided, it will return all the values anchored in the
	// provided time window. If max elements is also provided as part of the
	// lookup options it will return at most max elements. There is no
	// specifications on how that sample should be conducted.
	Objects(ctx context.Context, s *node.Node, p *predicate.Predicate, lo *LookupOptions, objs chan<- *triple.Object) error

	// Subject pushes to the provided channel the subjects for the give predicate
	// and object. The function does not return immediately but spawns a
	// goroutine to satisfy elements in the channel.
	//
	// Given a predicate and an object, this method retrieves the subjects of
	// triples that matches them. By default, it does not limit the maximum number
	// of possible subjects returned, unless properly specified by provided lookup
	// options.
	//
	// If the provided predicate is immutable it will return all the possible
	// subject values or the number of max elements specified. There is no
	// requirement on how to sample the returned max elements.
	//
	// If the predicate is an unanchored temporal triple and no time anchors are
	// provided in the lookup options, it will return all the available subjects.
	// If time anchors are provided, it will return all the values anchored in the
	// provided time window. If max elements is also provided as part of the
	// lookup options it will return the at most max elements. There is no
	// specifications on how that sample should be conducted.
	Subjects(ctx context.Context, p *predicate.Predicate, o *triple.Object, lo *LookupOptions, subs chan<- *node.Node) error

	// PredicatesForSubject pushes to the provided channel all the predicates
	// known for the given subject. The function does not return immediately but
	// spawns a goroutine to satisfy elements in the channel.
	//
	// If the lookup options provide a max number of elements the function will
	// return a sample of the available predicates. If time anchor bounds are
	// provided in the lookup options, only predicates matching the provided
	// type window would be return. Same sampling consideration apply if max
	// element is provided.
	PredicatesForSubject(ctx context.Context, s *node.Node, lo *LookupOptions, prds chan<- *predicate.Predicate) error

	// PredicatesForObject pushes to the provided channel all the predicates known
	// for the given object. The function returns immediately and spawns a go
	// routine to satisfy elements in the channel.
	//
	// If the lookup options provide a max number of elements the function will
	// return a sample of the available predicates. If time anchor bounds are
	// provided in the lookup options, only predicates matching the provided type
	// window would be return. Same sampling consideration apply if max element
	// is provided.
	PredicatesForObject(ctx context.Context, o *triple.Object, lo *LookupOptions, prds chan<- *predicate.Predicate) error

	// PredicatesForSubjectAndObject pushes to the provided channel all predicates
	// available for the given subject and object. The function does not return
	// immediately but spawns a goroutine to satisfy elements in the channel.
	//
	// If the lookup options provide a max number of elements the function will
	// return a sample of the available predicates. If time anchor bounds are
	// provided in the lookup options, only predicates matching the provided type
	// window would be return. Same sampling consideration apply if max element is
	// provided.
	PredicatesForSubjectAndObject(ctx context.Context, s *node.Node, o *triple.Object, lo *LookupOptions, prds chan<- *predicate.Predicate) error

	// TriplesForSubject pushes to the provided channel all triples available for
	// the given subject. The function does not return immediately but spawns a
	// goroutine to satisfy elements in the channel.
	//
	// If the lookup options provide a max number of elements the function will
	// return a sample of the available triples. If time anchor bounds are
	// provided in the lookup options, only predicates matching the provided type
	// window would be return. Same sampling consideration apply if max element is
	// provided.
	TriplesForSubject(ctx context.Context, s *node.Node, lo *LookupOptions, trpls chan<- *triple.Triple) error

	// TriplesForPredicate pushes to the provided channel all triples available
	// for the given predicate.The function does not return immediately but spawns
	// a goroutine to satisfy elements in the channel.
	//
	// If the lookup options provide a max number of elements the function will
	// return a sample of the available triples. If time anchor bounds are
	// provided in the lookup options, only predicates matching the provided type
	// window would be return. Same sampling consideration apply if max element is
	// provided.
	TriplesForPredicate(ctx context.Context, p *predicate.Predicate, lo *LookupOptions, trpls chan<- *triple.Triple) error

	// TriplesForObject pushes to the provided channel all triples available for
	// the given object. The function does not return immediately but spawns a
	// goroutine to satisfy elements in the channel.
	//
	// If the lookup options provide a max number of elements the function will
	// return a sample of the available triples. If time anchor bounds are
	// provided in the lookup options, only predicates matching the provided type
	// window would be return. Same sampling consideration apply if max element is
	// provided.
	TriplesForObject(ctx context.Context, o *triple.Object, lo *LookupOptions, trpls chan<- *triple.Triple) error

	// TriplesForSubjectAndPredicate pushes to the provided channel all triples
	// available for the given subject and predicate. The function does not return
	// immediately but spawns a goroutine to satisfy elements in the channel.
	//
	// If the lookup options provide a max number of elements the function will
	// return a sample of the available triples. If time anchor bounds are
	// provided in the lookup options, only predicates matching the provided type
	// window would be return. Same sampling consideration apply if max element is
	// provided.
	TriplesForSubjectAndPredicate(ctx context.Context, s *node.Node, p *predicate.Predicate, lo *LookupOptions, trpls chan<- *triple.Triple) error

	// TriplesForPredicateAndObject pushes to the provided channel all triples
	// available for the given predicate and object. The function does not return
	// immediately but spawns a goroutine to satisfy elements in the channel.
	//
	// If the lookup options provide a max number of elements the function will
	// return a sample of the available triples. If time anchor bounds are
	// provided in the lookup options, only predicates matching the provided type
	// window would be return. Same sampling consideration apply if max element is
	// provided.
	TriplesForPredicateAndObject(ctx context.Context, p *predicate.Predicate, o *triple.Object, lo *LookupOptions, trpls chan<- *triple.Triple) error

	// Exist checks if the provided triple exists on the store.
	Exist(ctx context.Context, t *triple.Triple) (bool, error)

	// Triples pushes to the provided channel all available triples in the graph.
	// The function does not return immediately but spawns a goroutine to satisfy
	// elements in the channel.
	Triples(ctx context.Context, lo *LookupOptions, trpls chan<- *triple.Triple) error
}
<commit_msg>Update misleading documentation<commit_after>// Copyright 2015 Google Inc. All rights reserved.
//
// 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 storage provides the abstraction to build drivers for BadWolf.
package storage

import (
	"bytes"
	"strconv"
	"time"

	"github.com/google/badwolf/triple"
	"github.com/google/badwolf/triple/node"
	"github.com/google/badwolf/triple/predicate"
	"golang.org/x/net/context"
)

// LookupOptions allows to specify the behavior of the lookup operations.
type LookupOptions struct {
	// MaxElements list the maximum number of elements to return. If not
	// set it returns all the lookup results.
	MaxElements int

	// LowerAnchor, if provided, represents the lower time anchor to be considered.
	LowerAnchor *time.Time

	// UpperAnchor, if provided, represents the upper time anchor to be considered.
	UpperAnchor *time.Time
}

// String returns a readable version of the LookupOptions instance.
func (l *LookupOptions) String() string {
	b := bytes.NewBufferString("<limit=")
	b.WriteString(strconv.Itoa(l.MaxElements))
	b.WriteString(", lower_anchor=")
	if l.LowerAnchor != nil {
		b.WriteString(l.LowerAnchor.String())
	} else {
		b.WriteString("nil")
	}
	b.WriteString(", upper_anchor=")
	if l.UpperAnchor != nil {
		b.WriteString(l.UpperAnchor.String())
	} else {
		b.WriteString("nil")
	}
	b.WriteString(">")
	return b.String()
}

// DefaultLookup provides the default lookup behavior.
var DefaultLookup = &LookupOptions{}

// Store interface describes the low lever API that allows to create new graphs.
type Store interface {
	// Name returns the ID of the backend being used.
	Name(ctx context.Context) string

	// Version returns the version of the driver implementation.
	Version(ctx context.Context) string

	// NewGraph creates a new graph. Creating an already existing graph
	// should return an error.
	NewGraph(ctx context.Context, id string) (Graph, error)

	// Graph returns an existing graph if available. Getting a non existing
	// graph should return an error.
	Graph(ctx context.Context, id string) (Graph, error)

	// DeleteGraph deletes an existing graph. Deleting a non existing graph
	// should return an error.
	DeleteGraph(ctx context.Context, id string) error

	// GraphNames returns the current available graph names in the store.
	GraphNames(ctx context.Context, names chan<- string) error
}

// Graph interface describes the low level API that storage drivers need
// to implement to provide a compliant graph storage that can be used with
// BadWolf.
//
// If you are implementing a driver or just using a low lever driver directly
// it is important for you to keep in mind that you will need to drain the
// provided channel. Otherwise you run the risk of leaking go routines.
type Graph interface {
	// ID returns the id for this graph.
	ID(ctx context.Context) string

	// AddTriples adds the triples to the storage. Adding a triple that already
	// exists should not fail.
	AddTriples(ctx context.Context, ts []*triple.Triple) error

	// RemoveTriples removes the triples from the storage. Removing triples that
	// are not present on the store should not fail.
	RemoveTriples(ctx context.Context, ts []*triple.Triple) error

	// Objects pushes to the provided channel the objects for the given object and
	// predicate. The function does not return immediately.
	//
	// Given a subject and a predicate, this method retrieves the objects of
	// triples that match them. By default, if does not limit the maximum number
	// of possible objects returned, unless properly specified by provided lookup
	// options.
	//
	// If the provided predicate is immutable it will return all the possible
	// subject values or the number of max elements specified. There is no
	// requirement on how to sample the returned max elements.
	//
	// If the predicate is an unanchored temporal triple and no time anchors are
	// provided in the lookup options, it will return all the available objects.
	// If time anchors are provided, it will return all the values anchored in the
	// provided time window. If max elements is also provided as part of the
	// lookup options it will return at most max elements. There is no
	// specifications on how that sample should be conducted.
	Objects(ctx context.Context, s *node.Node, p *predicate.Predicate, lo *LookupOptions, objs chan<- *triple.Object) error

	// Subject pushes to the provided channel the subjects for the give predicate
	// and object. The function does not return immediately. The caller is
	// expected to detach them into a go routine.
	//
	// Given a predicate and an object, this method retrieves the subjects of
	// triples that matches them. By default, it does not limit the maximum number
	// of possible subjects returned, unless properly specified by provided lookup
	// options.
	//
	// If the provided predicate is immutable it will return all the possible
	// subject values or the number of max elements specified. There is no
	// requirement on how to sample the returned max elements.
	//
	// If the predicate is an unanchored temporal triple and no time anchors are
	// provided in the lookup options, it will return all the available subjects.
	// If time anchors are provided, it will return all the values anchored in the
	// provided time window. If max elements is also provided as part of the
	// lookup options it will return the at most max elements. There is no
	// specifications on how that sample should be conducted.
	Subjects(ctx context.Context, p *predicate.Predicate, o *triple.Object, lo *LookupOptions, subs chan<- *node.Node) error

	// PredicatesForSubject pushes to the provided channel all the predicates
	// known for the given subject. The function does not return immediately.
	// The caller is expected to detach them into a go routine.
	//
	// If the lookup options provide a max number of elements the function will
	// return a sample of the available predicates. If time anchor bounds are
	// provided in the lookup options, only predicates matching the provided
	// type window would be return. Same sampling consideration apply if max
	// element is provided.
	PredicatesForSubject(ctx context.Context, s *node.Node, lo *LookupOptions, prds chan<- *predicate.Predicate) error

	// PredicatesForObject pushes to the provided channel all the predicates known
	// for the given object. The function does not return immediately. The caller
	// is expected to detach them into a go routine.
	//
	// If the lookup options provide a max number of elements the function will
	// return a sample of the available predicates. If time anchor bounds are
	// provided in the lookup options, only predicates matching the provided type
	// window would be return. Same sampling consideration apply if max element
	// is provided.
	PredicatesForObject(ctx context.Context, o *triple.Object, lo *LookupOptions, prds chan<- *predicate.Predicate) error

	// PredicatesForSubjectAndObject pushes to the provided channel all predicates
	// available for the given subject and object. The function does not return
	// immediately. The caller is expected to detach them into a go routine.
	//
	// If the lookup options provide a max number of elements the function will
	// return a sample of the available predicates. If time anchor bounds are
	// provided in the lookup options, only predicates matching the provided type
	// window would be return. Same sampling consideration apply if max element is
	// provided.
	PredicatesForSubjectAndObject(ctx context.Context, s *node.Node, o *triple.Object, lo *LookupOptions, prds chan<- *predicate.Predicate) error

	// TriplesForSubject pushes to the provided channel all triples available for
	// the given subject. The function does not return immediately. The caller
	// is expected to detach them into a go routine.
	//
	// If the lookup options provide a max number of elements the function will
	// return a sample of the available triples. If time anchor bounds are
	// provided in the lookup options, only predicates matching the provided type
	// window would be return. Same sampling consideration apply if max element is
	// provided.
	TriplesForSubject(ctx context.Context, s *node.Node, lo *LookupOptions, trpls chan<- *triple.Triple) error

	// TriplesForPredicate pushes to the provided channel all triples available
	// for the given predicate.The function does not return immediatel. The
	// caller is expected to detach them into a go routine.
	//
	// If the lookup options provide a max number of elements the function will
	// return a sample of the available triples. If time anchor bounds are
	// provided in the lookup options, only predicates matching the provided type
	// window would be return. Same sampling consideration apply if max element is
	// provided.
	TriplesForPredicate(ctx context.Context, p *predicate.Predicate, lo *LookupOptions, trpls chan<- *triple.Triple) error

	// TriplesForObject pushes to the provided channel all triples available for
	// the given object. The function does not return immediately. The caller is
	// expected to detach them into a go routine.
	//
	// If the lookup options provide a max number of elements the function will
	// return a sample of the available triples. If time anchor bounds are
	// provided in the lookup options, only predicates matching the provided type
	// window would be return. Same sampling consideration apply if max element is
	// provided.
	TriplesForObject(ctx context.Context, o *triple.Object, lo *LookupOptions, trpls chan<- *triple.Triple) error

	// TriplesForSubjectAndPredicate pushes to the provided channel all triples
	// available for the given subject and predicate. The function does not return
	// immediately. The caller is expected to detach them into a go routine.
	//
	// If the lookup options provide a max number of elements the function will
	// return a sample of the available triples. If time anchor bounds are
	// provided in the lookup options, only predicates matching the provided type
	// window would be return. Same sampling consideration apply if max element is
	// provided.
	TriplesForSubjectAndPredicate(ctx context.Context, s *node.Node, p *predicate.Predicate, lo *LookupOptions, trpls chan<- *triple.Triple) error

	// TriplesForPredicateAndObject pushes to the provided channel all triples
	// available for the given predicate and object. The function does not return
	// immediately. The caller is expected to detach them into a go routine.
	//
	// If the lookup options provide a max number of elements the function will
	// return a sample of the available triples. If time anchor bounds are
	// provided in the lookup options, only predicates matching the provided type
	// window would be return. Same sampling consideration apply if max element is
	// provided.
	TriplesForPredicateAndObject(ctx context.Context, p *predicate.Predicate, o *triple.Object, lo *LookupOptions, trpls chan<- *triple.Triple) error

	// Exist checks if the provided triple exists on the store.
	Exist(ctx context.Context, t *triple.Triple) (bool, error)

	// Triples pushes to the provided channel all available triples in the graph.
	// The function does not return immediately but spawns a goroutine to satisfy
	// elements in the channel.
	Triples(ctx context.Context, lo *LookupOptions, trpls chan<- *triple.Triple) error
}
<|endoftext|>


<commit_before>// Copyright 2018 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
//
//     https://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 storage provides the storage interfaces required by the various
// pieces of the CT monitor.
package storage

import (
	"context"

	"github.com/google/monologue/apicall"
	"github.com/google/monologue/ctlog"
)

// APICallWriter is an interface for storing individual calls to CT API
// endpoints.
type APICallWriter interface {
	WriteAPICall(ctx context.Context, l *ctlog.Log, apiCall *apicall.APICall) error
}
<commit_msg>Add STH writing to storage interfaces.<commit_after>// Copyright 2018 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
//
//     https://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 storage provides the storage interfaces required by the various
// pieces of the CT monitor.
package storage

import (
	"context"

	ct "github.com/google/certificate-transparency-go"
	"github.com/google/monologue/apicall"
	"github.com/google/monologue/ctlog"
)

// APICallWriter is an interface for storing individual calls to CT API
// endpoints.
type APICallWriter interface {
	WriteAPICall(ctx context.Context, l *ctlog.Log, apiCall *apicall.APICall) error
}

// STHWriter is an interface for storing STHs received from a CT Log.
type STHWriter interface {
	WriteSTH(ctx context.Context, l *ctlog.Log, sth *ct.SignedTreeHead, errs []error) error
}

// APICallSTHWriter represents a type that can store API Calls and store STHs.
type APICallSTHWriter interface {
	APICallWriter
	STHWriter
}
<|endoftext|>


<commit_before>// Package config for config files.
package client

// LibraryVersion specifies the current version of twilio-go.
const LibraryVersion = "0.22.2"
<commit_msg>Release v0.23.0<commit_after>// Package config for config files.
package client

// LibraryVersion specifies the current version of twilio-go.
const LibraryVersion = "0.23.0"
<|endoftext|>


<commit_before>//
// Copyright 2021, Sander van Harmelen
//
// 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 gitlab

import (
	"net/http"

	retryablehttp "github.com/hashicorp/go-retryablehttp"
)

// ClientOptionFunc can be used to customize a new GitLab API client.
type ClientOptionFunc func(*Client) error

// WithBaseURL sets the base URL for API requests to a custom endpoint.
func WithBaseURL(urlStr string) ClientOptionFunc {
	return func(c *Client) error {
		return c.setBaseURL(urlStr)
	}
}

// WithCustomBackoff can be used to configure a custom backoff policy.
func WithCustomBackoff(backoff retryablehttp.Backoff) ClientOptionFunc {
	return func(c *Client) error {
		c.client.Backoff = backoff
		return nil
	}
}

// WithCustomLeveledLogger can be used to configure a custom retryablehttp
// leveled logger.
func WithCustomLeveledLogger(leveledLogger retryablehttp.LeveledLogger) ClientOptionFunc {
	return func(c *Client) error {
		c.client.Logger = leveledLogger
		return nil
	}
}

// WithCustomLimiter injects a custom rate limiter to the client.
func WithCustomLimiter(limiter RateLimiter) ClientOptionFunc {
	return func(c *Client) error {
		c.configureLimiterOnce.Do(func() {
			c.limiter = limiter
		})
		return nil
	}
}

// WithCustomLogger can be used to configure a custom retryablehttp logger.
func WithCustomLogger(logger retryablehttp.Logger) ClientOptionFunc {
	return func(c *Client) error {
		c.client.Logger = logger
		return nil
	}
}

// WithCustomRetry can be used to configure a custom retry policy.
func WithCustomRetry(checkRetry retryablehttp.CheckRetry) ClientOptionFunc {
	return func(c *Client) error {
		c.client.CheckRetry = checkRetry
		return nil
	}
}

// WithHTTPClient can be used to configure a custom HTTP client.
func WithHTTPClient(httpClient *http.Client) ClientOptionFunc {
	return func(c *Client) error {
		c.client.HTTPClient = httpClient
		return nil
	}
}

// WithRequestLogHook can be used to configure a custom request log hook.
func WithRequestLogHook(hook retryablehttp.RequestLogHook) ClientOptionFunc {
	return func(c *Client) error {
		c.client.RequestLogHook = hook
		return nil
	}
}

// WithResponseLogHook can be used to configure a custom response log hook.
func WithResponseLogHook(hook retryablehttp.ResponseLogHook) ClientOptionFunc {
	return func(c *Client) error {
		c.client.ResponseLogHook = hook
		return nil
	}
}

// WithoutRetries disables the default retry logic.
func WithoutRetries() ClientOptionFunc {
	return func(c *Client) error {
		c.disableRetries = true
		return nil
	}
}
<commit_msg>add WithCustomRetryMax and WithCustomRetryWaitMinMax<commit_after>//
// Copyright 2021, Sander van Harmelen
//
// 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 gitlab

import (
	"net/http"
	"time"

	retryablehttp "github.com/hashicorp/go-retryablehttp"
)

// ClientOptionFunc can be used to customize a new GitLab API client.
type ClientOptionFunc func(*Client) error

// WithBaseURL sets the base URL for API requests to a custom endpoint.
func WithBaseURL(urlStr string) ClientOptionFunc {
	return func(c *Client) error {
		return c.setBaseURL(urlStr)
	}
}

// WithCustomBackoff can be used to configure a custom backoff policy.
func WithCustomBackoff(backoff retryablehttp.Backoff) ClientOptionFunc {
	return func(c *Client) error {
		c.client.Backoff = backoff
		return nil
	}
}

// WithCustomLeveledLogger can be used to configure a custom retryablehttp
// leveled logger.
func WithCustomLeveledLogger(leveledLogger retryablehttp.LeveledLogger) ClientOptionFunc {
	return func(c *Client) error {
		c.client.Logger = leveledLogger
		return nil
	}
}

// WithCustomLimiter injects a custom rate limiter to the client.
func WithCustomLimiter(limiter RateLimiter) ClientOptionFunc {
	return func(c *Client) error {
		c.configureLimiterOnce.Do(func() {
			c.limiter = limiter
		})
		return nil
	}
}

// WithCustomLogger can be used to configure a custom retryablehttp logger.
func WithCustomLogger(logger retryablehttp.Logger) ClientOptionFunc {
	return func(c *Client) error {
		c.client.Logger = logger
		return nil
	}
}

// WithCustomRetry can be used to configure a custom retry policy.
func WithCustomRetry(checkRetry retryablehttp.CheckRetry) ClientOptionFunc {
	return func(c *Client) error {
		c.client.CheckRetry = checkRetry
		return nil
	}
}

// WithCustomRetryMax can be used to configure a custom maximum number of retries.
func WithCustomRetryMax(retryMax int) ClientOptionFunc {
	return func(c *Client) error {
		c.client.RetryMax = retryMax
		return nil
	}
}

// WithCustomRetryWaitMinMax can be used to configure a custom maximum and minimum time to wait between retries.
func WithCustomRetryWaitMinMax(waitMin, waitMax time.Duration) ClientOptionFunc {
	return func(c *Client) error {
		c.client.RetryWaitMin = waitMin
		c.client.RetryWaitMax = waitMax
		return nil
	}
}

// WithHTTPClient can be used to configure a custom HTTP client.
func WithHTTPClient(httpClient *http.Client) ClientOptionFunc {
	return func(c *Client) error {
		c.client.HTTPClient = httpClient
		return nil
	}
}

// WithRequestLogHook can be used to configure a custom request log hook.
func WithRequestLogHook(hook retryablehttp.RequestLogHook) ClientOptionFunc {
	return func(c *Client) error {
		c.client.RequestLogHook = hook
		return nil
	}
}

// WithResponseLogHook can be used to configure a custom response log hook.
func WithResponseLogHook(hook retryablehttp.ResponseLogHook) ClientOptionFunc {
	return func(c *Client) error {
		c.client.ResponseLogHook = hook
		return nil
	}
}

// WithoutRetries disables the default retry logic.
func WithoutRetries() ClientOptionFunc {
	return func(c *Client) error {
		c.disableRetries = true
		return nil
	}
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/rsa"

	cryptorand "crypto/rand"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"errors"
	"github.com/Sirupsen/logrus"
	"github.com/hlandau/acme/acmeapi"
	"github.com/hlandau/acme/acmeapi/acmeutils"
	context "golang.org/x/net/context"
	"net"
	"strings"
	"sync"
	"time"
	"encoding/pem"
	"fmt"
)

const (
	SNI01_EXPIRE_TOKEN time.Duration = time.Minute * 10
)

type acmeStruct struct {
	serverAddress string
	privateKey    *rsa.PrivateKey
	client        *acmeapi.Client

	mutex                *sync.Mutex
	acmeauthDomainsMutex *sync.Mutex
	acmeAuthDomains      map[string]time.Time
}

func (this *acmeStruct) Init() {
	this.client = &acmeapi.Client{
		AccountKey:   this.privateKey,
		DirectoryURL: this.serverAddress,
	}

	this.mutex = &sync.Mutex{}

	this.acmeauthDomainsMutex = &sync.Mutex{}
	this.acmeAuthDomains = make(map[string]time.Time)
	this.CleanupTimer()
}

func (this *acmeStruct) RegisterEnsure(ctx context.Context) (err error) {
	reg := &acmeapi.Registration{}
	for i := 0; i < TRY_COUNT+1; i++ { // +1 count need for request latest agreement uri
		reg.AgreementURI = reg.LatestAgreementURI
		if reg.AgreementURI != "" {
			logrus.Info("Auto agree with terms:", reg.LatestAgreementURI)
		}
		err = this.client.UpsertRegistration(reg, ctx)
		if reg.AgreementURI != "" && err == nil {
			return
		}
	}
	return err
}

func (this *acmeStruct) Cleanup() {
	this.mutex.Lock()
	defer this.mutex.Unlock()

	now := time.Now()
	for token, expire := range this.acmeAuthDomains {
		if expire.Before(now) {
			delete(this.acmeAuthDomains, token)
		}
	}
}

func (this *acmeStruct) CleanupTimer() {
	this.Cleanup()
	time.AfterFunc(SNI01_EXPIRE_TOKEN, this.Cleanup)
}

func (this *acmeStruct) CreateCertificate(domain string) (cert *tls.Certificate, err error) {
	// Check suffix for avoid mutex sync in DeleteAcmeAuthDomain
	if strings.HasSuffix(domain, ".acme.invalid") {
		logrus.Debugf("Detect auth-domain mode for domain '%v'", domain)
		if this.DeleteAcmeAuthDomain(domain) {
			logrus.Debugf("Return self-signed certificate for domain '%v'", domain)
			return this.createCertificateSelfSigned(domain)
		} else {
			logrus.Debugf("Detect auth-domain is not present in list '%v'", domain)
			return nil, errors.New("Now allowed auth-domain")
		}
	}

	// check about we serve the domain
	ips, err := net.LookupIP(domain)
	if err != nil {
		logrus.Warnf("Can't lookup ip for domain '%v': %v", domain, err)
		return nil, errors.New("Can't lookup ip of the domain")
	}
	isLocalIP := false
checkLocalIP:
	for _, ip := range ips {
		for _, localIP := range localIPs {
			if ip.Equal(localIP) {
				isLocalIP = true
				break checkLocalIP
			}
		}
	}
	if !isLocalIP {
		logrus.Warnf("Domain have ip of other server. Domain '%v', Domain ips: %v, Server ips: %v", domain, ips, localIPs)
		return nil, errors.New("Domain have ip of other server.")
	}

	return this.createCertificateAcme(domain)
}

func (this *acmeStruct) createCertificateAcme(domain string) (cert *tls.Certificate, err error) {
	this.mutex.Lock()
	defer this.mutex.Unlock()

	var auth *acmeapi.Authorization

	ctx, cancelFunc := context.WithTimeout(context.Background(), LETSENCRYPT_CREATE_CERTIFICATE_TIMEOUT)
	defer cancelFunc()

	for i := 0; i < TRY_COUNT; i++ {
		auth, err = this.client.NewAuthorization(domain, ctx)
		if err == nil {
			break
		} else {
			logrus.Infof("Can't create new authorization for domain '%v': %v", domain, err)
			time.Sleep(RETRY_SLEEP)
		}
	}
	if err == nil {
		logrus.Infof("Create authorization for domain '%v'", domain)
	} else {
		logrus.Errorf("Can't create new authorization for domain '%v': %v", domain, err)
		return nil, errors.New("Can't create new authorization for domain")
	}

	if logrus.GetLevel() >= logrus.DebugLevel {
		challengeTypes := make([]string, len(auth.Challenges))
		for i := range auth.Challenges {
			challengeTypes[i] = auth.Challenges[i].Type
		}
		logrus.Debugf("Challenge types for domain '%v': %v. Challenge combinations: %v", domain, challengeTypes, auth.Combinations)
	}

	canAuthorize := false
	var challenge *acmeapi.Challenge
	for _, cmb := range auth.Combinations {
		if len(cmb) == 1 && auth.Challenges[cmb[0]].Type == "tls-sni-01" {
			canAuthorize = true
			challenge = auth.Challenges[cmb[0]]
			break
		}
	}
	if !canAuthorize {
		logrus.Errorf("Can't find good challange combination for domain: '%v'", domain)
		return nil, errors.New("Can't find good challange combination")
	}

	acmeHostName, err := acmeutils.TLSSNIHostname(this.privateKey, challenge.Token)
	if err == nil {
		logrus.Debugf("Create acme-auth hostname for domain '%v': %v", domain, acmeHostName)
	} else {
		logrus.Errorf("Can't create acme domain for domain '%v' token '%v': %v", domain, challenge.Token, err)
		return nil, errors.New("Can't create acme domain")
	}
	this.PutAcmeAuthDomain(acmeHostName)

	challengeResponse, err := acmeutils.ChallengeResponseJSON(this.privateKey, challenge.Token, challenge.Type)
	if err == nil {
		logrus.Debugf("Create challenge response for domain '%v'", domain)
	} else {
		logrus.Errorf("Can't create challenge response for domain '%v', token '%v', challenge type %v: %v",
			domain, challenge.Token, challenge.Type, err)
		return nil, errors.New("Can't create challenge response")
	}
	for i := 0; i < TRY_COUNT; i++ {
		err = this.client.RespondToChallenge(challenge, challengeResponse, this.privateKey, ctx)
		if err == nil {
			break
		} else {
			logrus.Info("Can't send response for challenge of domain '%v': %v", domain, err)
			time.Sleep(RETRY_SLEEP)
		}
	}
	if err == nil {
		logrus.Debugf("Send challenge response for domain '%v'", domain)
	} else {
		logrus.Errorf("Can't send response for challenge of domain '%v': %v", domain, err)
		return nil, errors.New("Can't send response for challenge")
	}

	for i := 0; i < TRY_COUNT; i++ {
		err = this.client.LoadChallenge(challenge, ctx)
		if err != nil {
			logrus.Infof("Can't load challenge for domain '%v': %v", domain, err)
			time.Sleep(RETRY_SLEEP)
		}
	}
	if err == nil {
		logrus.Debugf("Load challenge for domain '%v'", domain)
	} else {
		logrus.Errorf("Can't load challenge for domain '%v': %v", domain, err)
		return nil, errors.New("Can't load challenge")
	}

	// Generate CSR
	certKey, err := rsa.GenerateKey(cryptorand.Reader, PRIVATE_KEY_BITS)
	if err == nil {
		logrus.Debugf("Create private key for domain '%v'", domain)
	} else {
		logrus.Errorf("Can't create rsa key for domain '%v': %v", err)
		return nil, errors.New("Can't create rsa key")
	}
	certRequest := &x509.CertificateRequest{
		SignatureAlgorithm: x509.SHA256WithRSA,
		PublicKeyAlgorithm: x509.RSA,
		PublicKey:          &certKey.PublicKey,
		Subject:            pkix.Name{CommonName: domain},
		DNSNames:           []string{domain},
	}
	csrDER, err := x509.CreateCertificateRequest(cryptorand.Reader, certRequest, certKey)
	if err == nil {
		logrus.Debugf("Create CSR for domain '%v'", domain)
	} else {
		logrus.Errorf("Can't create csr for domain '%v': %v", domain, err)
		return nil, errors.New("Can't create csr")
	}

	var certResponse *acmeapi.Certificate
	for i := 0; i < TRY_COUNT; i++ {
		certResponse, err = this.client.RequestCertificate(csrDER, ctx)
		fmt.Printf("!!!\n%v\n", certResponse.ExtraCertificates)
		if err != nil {
			logrus.Infof("Can't get certificate for domain '%v': %v", domain, err)
			time.Sleep(RETRY_SLEEP)
		}
	}
	if err == nil {
		logrus.Infof("Get certificate for domain '%v'", domain)
	} else {
		logrus.Errorf("Can't get certificate for domain '%v': %v", domain, err)
		return nil, errors.New("Can't request certificate")
	}

	pemEncode := func(b []byte, t string) []byte {
		return pem.EncodeToMemory(&pem.Block{Bytes: b, Type: t})
	}
	certPEM := pem.EncodeToMemory(&pem.Block{Bytes: certResponse.Certificate, Type: "CERTIFICATE"})
	logrus.Debugf("CERT PEM:\n%s", certPEM)
	certKeyPEM := pemEncode(x509.MarshalPKCS1PrivateKey(certKey), "RSA PRIVATE KEY")

	tmpCert, err := tls.X509KeyPair(certPEM, certKeyPEM)
	if err == nil {
		logrus.Infof("Cert for domain '%v' parsed.", domain)
		cert = &tmpCert
	} else {
		logrus.Errorf("Can't parse cert for domain '%v': %v", domain, err)
		return nil, errors.New("Can't parse cert for domain")
	}

	return cert, nil
}

func (this *acmeStruct) createCertificateSelfSigned(domain string) (cert *tls.Certificate, err error) {
	derCert, privateKey, err := acmeutils.CreateTLSSNICertificate(domain)
	if err != nil {
		logrus.Errorf("Can't create tls-sni-01 self-signed certificate for '%v': %v", domain, err)
		return nil, err
	}

	cert = &tls.Certificate{}
	cert.Certificate = [][]byte{derCert}
	cert.PrivateKey = privateKey
	return cert, nil
}

func (this *acmeStruct) PutAcmeAuthDomain(domain string) {
	this.acmeauthDomainsMutex.Lock()
	defer this.acmeauthDomainsMutex.Unlock()

	logrus.Debug("Put acme auth domain:", domain)
	this.acmeAuthDomains[domain] = time.Now().Add(SNI01_EXPIRE_TOKEN)
}

func (this *acmeStruct) DeleteAcmeAuthDomain(domain string) bool {
	this.acmeauthDomainsMutex.Lock()
	defer this.acmeauthDomainsMutex.Unlock()

	logrus.Debug("Delete acme auth domain:", domain)
	_, ok := this.acmeAuthDomains[domain]
	if ok {
		delete(this.acmeAuthDomains, domain)
	}
	return ok
}
<commit_msg>import context<commit_after>package main

import (
	"crypto/rsa"

	cryptorand "crypto/rand"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"errors"
	"github.com/Sirupsen/logrus"
	"github.com/hlandau/acme/acmeapi"
	"github.com/hlandau/acme/acmeapi/acmeutils"
	"context"
	"net"
	"strings"
	"sync"
	"time"
	"encoding/pem"
)

const (
	SNI01_EXPIRE_TOKEN time.Duration = time.Minute * 10
)

type acmeStruct struct {
	serverAddress string
	privateKey    *rsa.PrivateKey
	client        *acmeapi.Client

	mutex                *sync.Mutex
	acmeauthDomainsMutex *sync.Mutex
	acmeAuthDomains      map[string]time.Time
}

func (this *acmeStruct) Init() {
	this.client = &acmeapi.Client{
		AccountKey:   this.privateKey,
		DirectoryURL: this.serverAddress,
	}

	this.mutex = &sync.Mutex{}

	this.acmeauthDomainsMutex = &sync.Mutex{}
	this.acmeAuthDomains = make(map[string]time.Time)
	this.CleanupTimer()
}

func (this *acmeStruct) RegisterEnsure(ctx context.Context) (err error) {
	reg := &acmeapi.Registration{}
	for i := 0; i < TRY_COUNT+1; i++ { // +1 count need for request latest agreement uri
		reg.AgreementURI = reg.LatestAgreementURI
		if reg.AgreementURI != "" {
			logrus.Info("Auto agree with terms:", reg.LatestAgreementURI)
		}
		err = this.client.UpsertRegistration(reg, ctx)
		if reg.AgreementURI != "" && err == nil {
			return
		}
	}
	return err
}

func (this *acmeStruct) Cleanup() {
	this.mutex.Lock()
	defer this.mutex.Unlock()

	now := time.Now()
	for token, expire := range this.acmeAuthDomains {
		if expire.Before(now) {
			delete(this.acmeAuthDomains, token)
		}
	}
}

func (this *acmeStruct) CleanupTimer() {
	this.Cleanup()
	time.AfterFunc(SNI01_EXPIRE_TOKEN, this.Cleanup)
}

func (this *acmeStruct) CreateCertificate(domain string) (cert *tls.Certificate, err error) {
	// Check suffix for avoid mutex sync in DeleteAcmeAuthDomain
	if strings.HasSuffix(domain, ".acme.invalid") {
		logrus.Debugf("Detect auth-domain mode for domain '%v'", domain)
		if this.DeleteAcmeAuthDomain(domain) {
			logrus.Debugf("Return self-signed certificate for domain '%v'", domain)
			return this.createCertificateSelfSigned(domain)
		} else {
			logrus.Debugf("Detect auth-domain is not present in list '%v'", domain)
			return nil, errors.New("Now allowed auth-domain")
		}
	}

	// check about we serve the domain
	ips, err := net.LookupIP(domain)
	if err != nil {
		logrus.Warnf("Can't lookup ip for domain '%v': %v", domain, err)
		return nil, errors.New("Can't lookup ip of the domain")
	}
	isLocalIP := false
checkLocalIP:
	for _, ip := range ips {
		for _, localIP := range localIPs {
			if ip.Equal(localIP) {
				isLocalIP = true
				break checkLocalIP
			}
		}
	}
	if !isLocalIP {
		logrus.Warnf("Domain have ip of other server. Domain '%v', Domain ips: %v, Server ips: %v", domain, ips, localIPs)
		return nil, errors.New("Domain have ip of other server.")
	}

	return this.createCertificateAcme(domain)
}

func (this *acmeStruct) createCertificateAcme(domain string) (cert *tls.Certificate, err error) {
	this.mutex.Lock()
	defer this.mutex.Unlock()

	var auth *acmeapi.Authorization

	ctx, cancelFunc := context.WithTimeout(context.Background(), LETSENCRYPT_CREATE_CERTIFICATE_TIMEOUT)
	defer cancelFunc()

	for i := 0; i < TRY_COUNT; i++ {
		auth, err = this.client.NewAuthorization(domain, ctx)
		if err == nil {
			break
		} else {
			logrus.Infof("Can't create new authorization for domain '%v': %v", domain, err)
			time.Sleep(RETRY_SLEEP)
		}
	}
	if err == nil {
		logrus.Infof("Create authorization for domain '%v'", domain)
	} else {
		logrus.Errorf("Can't create new authorization for domain '%v': %v", domain, err)
		return nil, errors.New("Can't create new authorization for domain")
	}

	if logrus.GetLevel() >= logrus.DebugLevel {
		challengeTypes := make([]string, len(auth.Challenges))
		for i := range auth.Challenges {
			challengeTypes[i] = auth.Challenges[i].Type
		}
		logrus.Debugf("Challenge types for domain '%v': %v. Challenge combinations: %v", domain, challengeTypes, auth.Combinations)
	}

	canAuthorize := false
	var challenge *acmeapi.Challenge
	for _, cmb := range auth.Combinations {
		if len(cmb) == 1 && auth.Challenges[cmb[0]].Type == "tls-sni-01" {
			canAuthorize = true
			challenge = auth.Challenges[cmb[0]]
			break
		}
	}
	if !canAuthorize {
		logrus.Errorf("Can't find good challange combination for domain: '%v'", domain)
		return nil, errors.New("Can't find good challange combination")
	}

	acmeHostName, err := acmeutils.TLSSNIHostname(this.privateKey, challenge.Token)
	if err == nil {
		logrus.Debugf("Create acme-auth hostname for domain '%v': %v", domain, acmeHostName)
	} else {
		logrus.Errorf("Can't create acme domain for domain '%v' token '%v': %v", domain, challenge.Token, err)
		return nil, errors.New("Can't create acme domain")
	}
	this.PutAcmeAuthDomain(acmeHostName)

	challengeResponse, err := acmeutils.ChallengeResponseJSON(this.privateKey, challenge.Token, challenge.Type)
	if err == nil {
		logrus.Debugf("Create challenge response for domain '%v'", domain)
	} else {
		logrus.Errorf("Can't create challenge response for domain '%v', token '%v', challenge type %v: %v",
			domain, challenge.Token, challenge.Type, err)
		return nil, errors.New("Can't create challenge response")
	}
	for i := 0; i < TRY_COUNT; i++ {
		err = this.client.RespondToChallenge(challenge, challengeResponse, this.privateKey, ctx)
		if err == nil {
			break
		} else {
			logrus.Info("Can't send response for challenge of domain '%v': %v", domain, err)
			time.Sleep(RETRY_SLEEP)
		}
	}
	if err == nil {
		logrus.Debugf("Send challenge response for domain '%v'", domain)
	} else {
		logrus.Errorf("Can't send response for challenge of domain '%v': %v", domain, err)
		return nil, errors.New("Can't send response for challenge")
	}

	for i := 0; i < TRY_COUNT; i++ {
		err = this.client.LoadChallenge(challenge, ctx)
		if err != nil {
			logrus.Infof("Can't load challenge for domain '%v': %v", domain, err)
			time.Sleep(RETRY_SLEEP)
		}
	}
	if err == nil {
		logrus.Debugf("Load challenge for domain '%v'", domain)
	} else {
		logrus.Errorf("Can't load challenge for domain '%v': %v", domain, err)
		return nil, errors.New("Can't load challenge")
	}

	// Generate CSR
	certKey, err := rsa.GenerateKey(cryptorand.Reader, PRIVATE_KEY_BITS)
	if err == nil {
		logrus.Debugf("Create private key for domain '%v'", domain)
	} else {
		logrus.Errorf("Can't create rsa key for domain '%v': %v", err)
		return nil, errors.New("Can't create rsa key")
	}
	certRequest := &x509.CertificateRequest{
		SignatureAlgorithm: x509.SHA256WithRSA,
		PublicKeyAlgorithm: x509.RSA,
		PublicKey:          &certKey.PublicKey,
		Subject:            pkix.Name{CommonName: domain},
		DNSNames:           []string{domain},
	}
	csrDER, err := x509.CreateCertificateRequest(cryptorand.Reader, certRequest, certKey)
	if err == nil {
		logrus.Debugf("Create CSR for domain '%v'", domain)
	} else {
		logrus.Errorf("Can't create csr for domain '%v': %v", domain, err)
		return nil, errors.New("Can't create csr")
	}

	var certResponse *acmeapi.Certificate
	for i := 0; i < TRY_COUNT; i++ {
		certResponse, err = this.client.RequestCertificate(csrDER, ctx)
		if err != nil {
			logrus.Infof("Can't get certificate for domain '%v': %v", domain, err)
			time.Sleep(RETRY_SLEEP)
		}
	}
	if err == nil {
		logrus.Infof("Get certificate for domain '%v'", domain)
	} else {
		logrus.Errorf("Can't get certificate for domain '%v': %v", domain, err)
		return nil, errors.New("Can't request certificate")
	}

	pemEncode := func(b []byte, t string) []byte {
		return pem.EncodeToMemory(&pem.Block{Bytes: b, Type: t})
	}
	certPEM := pem.EncodeToMemory(&pem.Block{Bytes: certResponse.Certificate, Type: "CERTIFICATE"})
	logrus.Debugf("CERT PEM:\n%s", certPEM)
	certKeyPEM := pemEncode(x509.MarshalPKCS1PrivateKey(certKey), "RSA PRIVATE KEY")

	tmpCert, err := tls.X509KeyPair(certPEM, certKeyPEM)
	if err == nil {
		logrus.Infof("Cert for domain '%v' parsed.", domain)
		cert = &tmpCert
	} else {
		logrus.Errorf("Can't parse cert for domain '%v': %v", domain, err)
		return nil, errors.New("Can't parse cert for domain")
	}

	return cert, nil
}

func (this *acmeStruct) createCertificateSelfSigned(domain string) (cert *tls.Certificate, err error) {
	derCert, privateKey, err := acmeutils.CreateTLSSNICertificate(domain)
	if err != nil {
		logrus.Errorf("Can't create tls-sni-01 self-signed certificate for '%v': %v", domain, err)
		return nil, err
	}

	cert = &tls.Certificate{}
	cert.Certificate = [][]byte{derCert}
	cert.PrivateKey = privateKey
	return cert, nil
}

func (this *acmeStruct) PutAcmeAuthDomain(domain string) {
	this.acmeauthDomainsMutex.Lock()
	defer this.acmeauthDomainsMutex.Unlock()

	logrus.Debug("Put acme auth domain:", domain)
	this.acmeAuthDomains[domain] = time.Now().Add(SNI01_EXPIRE_TOKEN)
}

func (this *acmeStruct) DeleteAcmeAuthDomain(domain string) bool {
	this.acmeauthDomainsMutex.Lock()
	defer this.acmeauthDomainsMutex.Unlock()

	logrus.Debug("Delete acme auth domain:", domain)
	_, ok := this.acmeAuthDomains[domain]
	if ok {
		delete(this.acmeAuthDomains, domain)
	}
	return ok
}
<|endoftext|>


<commit_before>package smtpd

import (
	"flag"
	"fmt"
	"io/ioutil"
	"net"
	"net/smtp"
	"os"
	"os/exec"
	"path/filepath"
	"testing"
	"time"
)

var controlTestConfig string = `
servers:
- protocol: tcp
  address: 127.0.0.1:30025
logging:
  syslogfacility: local1
`

const (
	gomsfgaction = "GOMS_FG_ACTION"
)

func sendTestMail(t *testing.T) {

	conn, err := net.DialTimeout("tcp", "127.0.0.1:30025", 2*time.Second)
	if err != nil {
		t.Fatalf("Could not dial to initiate connection: %v", err)
	}
	c, err := smtp.NewClient(conn, "localhost")
	// Connect to the local SMTP server.
	// c, err := smtp.Dial("127.0.0.1:30025")
	if err != nil {
		t.Fatalf("Could not connect to local SMTP server: %v", err)
	}

	timeout := time.AfterFunc(10*time.Second, func() {
		t.Log("[FATAL] Abort after timeout")
		c.Close()
	})
	defer timeout.Stop()

	if err := c.Mail("sender@example.org"); err != nil {
		t.Fatalf("Could not send MAIL: %v", err)
	}

	if err := c.Rcpt("recipient@example.net"); err != nil {
		t.Fatalf("Could not send RCPT: %v", err)
	}

	// Send the email body.
	wc, err := c.Data()
	if err != nil {
		t.Fatalf("Could not send DATA: %v", err)
	}
	_, err = fmt.Fprintf(wc, "This is the email body")
	if err != nil {
		t.Fatalf("Could not send body: %v", err)
	}
	if err = wc.Close(); err != nil {
		t.Fatalf("Could not close mail transaction: %v", err)
	}
	// Send the QUIT command and close the connection.
	if err = c.Quit(); err != nil {
		t.Fatalf("Could not send QUIT: %v", err)
	}
}

func flagParse(args []string) {
	saveArgs := os.Args
	os.Args = args
	flag.Parse()
	os.Args = saveArgs
}

func waitForPidFile(t *testing.T, pidfn string, shouldExist bool) {
	correct := false
	for i := 1; i < 20; i++ {
		if _, err := os.Stat(pidfn); shouldExist == (err == nil || !os.IsNotExist(err)) {
			correct = true
			break
		}
		time.Sleep(50 * time.Millisecond)
	}
	if !correct {
		if shouldExist {
			t.Fatalf("Pidfile not present: %v", pidfn)
		} else {
			t.Fatalf("Pidfile not deleted: %v", pidfn)
		}
	}
}

// this test needs to be first
func TestDaemonize(t *testing.T) {
	dir, err := ioutil.TempDir("", "gomstest")
	if err != nil {
		t.Fatalf("Could not create temporary directory: %v", err)
	}
	defer os.RemoveAll(dir)

	conffn := filepath.Join(dir, "goms.conf")
	if err := ioutil.WriteFile(conffn, []byte(controlTestConfig), 0666); err != nil {
		t.Fatalf("Could not create config file: %v", err)
	}
	pidfn := filepath.Join(dir, "goms.pid")

	flagParse([]string{"goms", "-c", conffn, "-p", pidfn})
	Run(nil)

	waitForPidFile(t, pidfn, true)

	time.Sleep(100 * time.Millisecond)

	sendTestMail(t)

	time.Sleep(100 * time.Millisecond)
	flagParse([]string{"goms", "-c", conffn, "-p", pidfn, "-s", "reload"})
	Run(nil)

	waitForPidFile(t, pidfn, true)

	time.Sleep(20 * time.Millisecond)

	sendTestMail(t)

	time.Sleep(100 * time.Millisecond)
	flagParse([]string{"goms", "-c", conffn, "-p", pidfn, "-s", "stop", "-test.v", "-test.run", "TestDaeemonize"})
	Run(nil)

	waitForPidFile(t, pidfn, false)
}

func TestForeground(t *testing.T) {
	dir, err := ioutil.TempDir("", "gomstest")
	if err != nil {
		t.Fatalf("Could not create temporary directory: %v", err)
	}
	defer os.RemoveAll(dir)

	conffn := filepath.Join(dir, "goms.conf")
	if err := ioutil.WriteFile(conffn, []byte(controlTestConfig), 0666); err != nil {
		t.Fatalf("Could not create config file: %v", err)
	}
	pidfn := filepath.Join(dir, "goms.pid")

	c := &Control{
		quit:     make(chan struct{}),
		dummyRun: true,
	}
	c.wg.Add(1)

	switch os.Getenv(gomsfgaction) {
	case "signalnotrunning":
		flagParse([]string{"goms", "-c", conffn, "-p", pidfn, "-s", "reload"})
	case "signalunknown":
		flagParse([]string{"goms", "-c", conffn, "-p", pidfn, "-s", "unknown"})
	case "badconffile":
		flagParse([]string{"goms", "-c", "////", "-p", pidfn, "-f"})
	case "badpidfile":
		flagParse([]string{"goms", "-c", conffn, "-p", "////"})
	case "noconffile":
		flagParse([]string{"goms", "-c", conffn + "-unknown", "-p", pidfn, "-f"})
	default:
		flagParse([]string{"goms", "-c", conffn, "-p", pidfn, "-f"})
		c.dummyRun = false
	}

	go Run(c)

	time.Sleep(200 * time.Millisecond)

	if c.dummyRun {
		os.Exit(0)
	}

	sendTestMail(t)
	close(c.quit)
	c.wg.Wait()
}

func testForegroundAction(t *testing.T, action string) {
	cmd := exec.Command(os.Args[0], "-test.run=TestForeground")
	cmd.Env = append(os.Environ(), fmt.Sprintf("%s=%s", gomsfgaction, action))
	err := cmd.Run()
	if e, ok := err.(*exec.ExitError); ok && !e.Success() {
		return
	}
	t.Fatalf("TestLaunchErrors test '%s' ran with err %v, want exit status 1", action, err)
}

func TestLaunchErrors(t *testing.T) {
	testForegroundAction(t, "signalnotrunning")
	// testForegroundAction(t, "signalunknown")
	testForegroundAction(t, "badconffile")
	testForegroundAction(t, "badpidfile")
	testForegroundAction(t, "noconffile")
}
<commit_msg>Retry on connection to eliminate race condition<commit_after>package smtpd

import (
	"flag"
	"fmt"
	"io/ioutil"
	"net"
	"net/smtp"
	"os"
	"os/exec"
	"path/filepath"
	"testing"
	"time"
)

var controlTestConfig string = `
servers:
- protocol: tcp
  address: 127.0.0.1:30025
logging:
  syslogfacility: local1
`

const (
	gomsfgaction = "GOMS_FG_ACTION"
)

func sendTestMail(t *testing.T) {

	var conn net.Conn
	var err error
	retries := 0

	for retries < 20 {
		conn, err = net.DialTimeout("tcp", "127.0.0.1:30025", 2*time.Second)
		if err == nil {
			break
		}
		retries++
		time.Sleep(100 * time.Millisecond)
	}

	if err != nil {
		t.Fatalf("Could not dial to initiate connection: %v", err)
	}

	c, err := smtp.NewClient(conn, "localhost")
	// Connect to the local SMTP server.
	// c, err := smtp.Dial("127.0.0.1:30025")
	if err != nil {
		t.Fatalf("Could not connect to local SMTP server: %v", err)
	}

	timeout := time.AfterFunc(10*time.Second, func() {
		t.Log("[FATAL] Abort after timeout")
		c.Close()
	})
	defer timeout.Stop()

	if err := c.Mail("sender@example.org"); err != nil {
		t.Fatalf("Could not send MAIL: %v", err)
	}

	if err := c.Rcpt("recipient@example.net"); err != nil {
		t.Fatalf("Could not send RCPT: %v", err)
	}

	// Send the email body.
	wc, err := c.Data()
	if err != nil {
		t.Fatalf("Could not send DATA: %v", err)
	}
	_, err = fmt.Fprintf(wc, "This is the email body")
	if err != nil {
		t.Fatalf("Could not send body: %v", err)
	}
	if err = wc.Close(); err != nil {
		t.Fatalf("Could not close mail transaction: %v", err)
	}
	// Send the QUIT command and close the connection.
	if err = c.Quit(); err != nil {
		t.Fatalf("Could not send QUIT: %v", err)
	}
}

func flagParse(args []string) {
	saveArgs := os.Args
	os.Args = args
	flag.Parse()
	os.Args = saveArgs
}

func waitForPidFile(t *testing.T, pidfn string, shouldExist bool) {
	correct := false
	for i := 1; i < 20; i++ {
		if _, err := os.Stat(pidfn); shouldExist == (err == nil || !os.IsNotExist(err)) {
			correct = true
			break
		}
		time.Sleep(50 * time.Millisecond)
	}
	if !correct {
		if shouldExist {
			t.Fatalf("Pidfile not present: %v", pidfn)
		} else {
			t.Fatalf("Pidfile not deleted: %v", pidfn)
		}
	}
}

// this test needs to be first
func TestDaemonize(t *testing.T) {
	dir, err := ioutil.TempDir("", "gomstest")
	if err != nil {
		t.Fatalf("Could not create temporary directory: %v", err)
	}
	defer os.RemoveAll(dir)

	conffn := filepath.Join(dir, "goms.conf")
	if err := ioutil.WriteFile(conffn, []byte(controlTestConfig), 0666); err != nil {
		t.Fatalf("Could not create config file: %v", err)
	}
	pidfn := filepath.Join(dir, "goms.pid")

	flagParse([]string{"goms", "-c", conffn, "-p", pidfn})
	Run(nil)

	waitForPidFile(t, pidfn, true)

	time.Sleep(100 * time.Millisecond)

	sendTestMail(t)

	time.Sleep(100 * time.Millisecond)
	flagParse([]string{"goms", "-c", conffn, "-p", pidfn, "-s", "reload"})
	Run(nil)

	waitForPidFile(t, pidfn, true)

	time.Sleep(100 * time.Millisecond)

	sendTestMail(t)

	time.Sleep(100 * time.Millisecond)
	flagParse([]string{"goms", "-c", conffn, "-p", pidfn, "-s", "stop", "-test.v", "-test.run", "TestDaeemonize"})
	Run(nil)

	waitForPidFile(t, pidfn, false)
}

func TestForeground(t *testing.T) {
	dir, err := ioutil.TempDir("", "gomstest")
	if err != nil {
		t.Fatalf("Could not create temporary directory: %v", err)
	}
	defer os.RemoveAll(dir)

	conffn := filepath.Join(dir, "goms.conf")
	if err := ioutil.WriteFile(conffn, []byte(controlTestConfig), 0666); err != nil {
		t.Fatalf("Could not create config file: %v", err)
	}
	pidfn := filepath.Join(dir, "goms.pid")

	c := &Control{
		quit:     make(chan struct{}),
		dummyRun: true,
	}
	c.wg.Add(1)

	switch os.Getenv(gomsfgaction) {
	case "signalnotrunning":
		flagParse([]string{"goms", "-c", conffn, "-p", pidfn, "-s", "reload"})
	case "signalunknown":
		flagParse([]string{"goms", "-c", conffn, "-p", pidfn, "-s", "unknown"})
	case "badconffile":
		flagParse([]string{"goms", "-c", "////", "-p", pidfn, "-f"})
	case "badpidfile":
		flagParse([]string{"goms", "-c", conffn, "-p", "////"})
	case "noconffile":
		flagParse([]string{"goms", "-c", conffn + "-unknown", "-p", pidfn, "-f"})
	default:
		flagParse([]string{"goms", "-c", conffn, "-p", pidfn, "-f"})
		c.dummyRun = false
	}

	go Run(c)

	time.Sleep(200 * time.Millisecond)

	if c.dummyRun {
		os.Exit(0)
	}

	sendTestMail(t)
	close(c.quit)
	c.wg.Wait()
}

func testForegroundAction(t *testing.T, action string) {
	cmd := exec.Command(os.Args[0], "-test.run=TestForeground")
	cmd.Env = append(os.Environ(), fmt.Sprintf("%s=%s", gomsfgaction, action))
	err := cmd.Run()
	if e, ok := err.(*exec.ExitError); ok && !e.Success() {
		return
	}
	t.Fatalf("TestLaunchErrors test '%s' ran with err %v, want exit status 1", action, err)
}

func TestLaunchErrors(t *testing.T) {
	testForegroundAction(t, "signalnotrunning")
	// testForegroundAction(t, "signalunknown")
	testForegroundAction(t, "badconffile")
	testForegroundAction(t, "badpidfile")
	testForegroundAction(t, "noconffile")
}
<|endoftext|>


<commit_before>package design

import (
       . "github.com/goadesign/goa/design"
       . "github.com/goadesign/goa/design/apidsl"
)<commit_msg>add glide yaml pkg config file<commit_after>package design

import (
       . "github.com/goadesign/goa/design"
       . "github.com/goadesign/goa/design/apidsl"
)

var _ = API('config', func() {
    Routing(POST)

}) <|endoftext|>


<commit_before>package main

import (
	"os"
	"github.com/mohong122/ip2region/binding/golang/ip2region"
	"bufio"
	"fmt"
	"strings"
	"errors"
	"time"

)

func main() {

	db := os.Args[1]

	_,err:= os.Stat(db)
	if os.IsNotExist(err){
		panic("not found db " + db)
	}

	region, err := ip2region.New(db)
	defer region.Close()
	fmt.Println(`initializing
+-------------------------------------------------------+
| ip2region test script                                 |
| format 'ip type'                                      |
| type option 'b-tree','binary','memory' default b-tree |
| Type 'quit' to exit program                           |
+-------------------------------------------------------+`)

	reader := bufio.NewReader(os.Stdin)
	for {
		fmt.Print("ip2reginon >> ")
		data, _, _ := reader.ReadLine()
		begin:= time.Now()
		commands := strings.Fields(string(data))
		ip := ip2region.IpInfo{}
		len := len(commands)
		if len == 0{
			continue
		}

		if commands[0] == "quit"{
			break
		}

		if !(len > 1) {
			commands = append(commands, "b-tree")
		}
		switch commands[1] {
		case "b-tree":
			ip, err = region.BtreeSearch(commands[0])
		case "binary":
			ip, err = region.BinarySearch(commands[0])
		case "memory":
			ip, err = region.MemorySearch(commands[0])
		default:
			err = errors.New("parameter error")
		}

		if err != nil {

			fmt.Println( fmt.Sprintf("\x1b[0;31m%s\x1b[0m",err.Error()))
		}else{
			fmt.Println( fmt.Sprintf("\x1b[0;32m%s  %s\x1b[0m",ip.String(),time.Since(begin).String()))
		}
	}
}
<commit_msg>Update main.go<commit_after>package main

import (
	"os"
	"github.com/lionsoul2014/ip2region/binding/golang/ip2region"
	"bufio"
	"fmt"
	"strings"
	"errors"
	"time"

)

func main() {

	db := os.Args[1]

	_,err:= os.Stat(db)
	if os.IsNotExist(err){
		panic("not found db " + db)
	}

	region, err := ip2region.New(db)
	defer region.Close()
	fmt.Println(`initializing
+-------------------------------------------------------+
| ip2region test script                                 |
| format 'ip type'                                      |
| type option 'b-tree','binary','memory' default b-tree |
| Type 'quit' to exit program                           |
+-------------------------------------------------------+`)

	reader := bufio.NewReader(os.Stdin)
	for {
		fmt.Print("ip2reginon >> ")
		data, _, _ := reader.ReadLine()
		begin:= time.Now()
		commands := strings.Fields(string(data))
		ip := ip2region.IpInfo{}
		len := len(commands)
		if len == 0{
			continue
		}

		if commands[0] == "quit"{
			break
		}

		if !(len > 1) {
			commands = append(commands, "b-tree")
		}
		switch commands[1] {
		case "b-tree":
			ip, err = region.BtreeSearch(commands[0])
		case "binary":
			ip, err = region.BinarySearch(commands[0])
		case "memory":
			ip, err = region.MemorySearch(commands[0])
		default:
			err = errors.New("parameter error")
		}

		if err != nil {

			fmt.Println( fmt.Sprintf("\x1b[0;31m%s\x1b[0m",err.Error()))
		}else{
			fmt.Println( fmt.Sprintf("\x1b[0;32m%s  %s\x1b[0m",ip.String(),time.Since(begin).String()))
		}
	}
}
<|endoftext|>


<commit_before>package commands

import (
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
	"strings"

	log "github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
	"gopkg.in/yaml.v2"

	"github.com/ailispaw/talk2docker/api"
	"github.com/ailispaw/talk2docker/client"
)

var cmdCompose = &cobra.Command{
	Use:     "compose <PATH/TO/YAML> [NAME...]",
	Aliases: []string{"fig", "create"},
	Short:   "Compose containers",
	Long:    APP_NAME + " compose - Compose containers",
	Run:     composeContainers,
}

var cmdComposeContainers = &cobra.Command{
	Use:     "compose <PATH/TO/YAML> [NAME...]",
	Aliases: []string{"fig", "create"},
	Short:   "Compose containers",
	Long:    APP_NAME + " container compose - Compose containers",
	Run:     composeContainers,
}

func init() {
	cmdContainer.AddCommand(cmdComposeContainers)
}

type Composer struct {
	Build string `yaml:"build"`

	Ports   []string `yaml:"ports"`
	Volumes []string `yaml:"volumes"`

	// api.Config
	Hostname     string   `yaml:"hostname"`
	Domainname   string   `yaml:"domainname"`
	User         string   `yaml:"user"`
	Memory       int64    `yaml:"mem_limit"`
	MemorySwap   int64    `yaml:"mem_swap"`
	CpuShares    int64    `yaml:"cpu_shares"`
	Cpuset       string   `yaml:"cpuset"`
	ExposedPorts []string `yaml:"expose"`
	Tty          bool     `yaml:"tty"`
	OpenStdin    bool     `yaml:"stdin_open"`
	Env          []string `yaml:"environment"`
	Cmd          []string `yaml:"command"`
	Image        string   `yaml:"image"`
	WorkingDir   string   `yaml:"working_dir"`
	Entrypoint   string   `yaml:"entrypoint"`
	MacAddress   string   `yaml:"mac_address"`

	// api.HostConfig
	Privileged      bool     `yaml:"privileged"`
	Links           []string `yaml:"links"`
	ExternalLinks   []string `yaml:"external_links"`
	PublishAllPorts bool     `yaml:"publish_all"`
	Dns             []string `yaml:"dns"`
	DnsSearch       []string `yaml:"dns_search"`
	ExtraHosts      []string `yaml:"add_host"`
	VolumesFrom     []string `yaml:"volumes_from"`
	Devices         []string `yaml:"device"`
	NetworkMode     string   `yaml:"net"`
	IpcMode         string   `yaml:"ipc"`
	PidMode         string   `yaml:"pid"`
	CapAdd          []string `yaml:"cap_add"`
	CapDrop         []string `yaml:"cap_drop"`
	RestartPolicy   string   `yaml:"restart"`
	SecurityOpt     []string `yaml:"security_opt"`
	ReadonlyRootfs  bool     `yaml:"read_only"`
}

func composeContainers(ctx *cobra.Command, args []string) {
	if len(args) < 1 {
		ErrorExit(ctx, "Needs an argument <PATH/TO/YAML> to compose containers")
	}

	path := os.ExpandEnv(args[0])

	var names []string
	if len(args) > 1 {
		names = args[1:]
	}

	data, err := ioutil.ReadFile(path)
	if err != nil {
		log.Fatal(err)
	}

	var composers map[string]Composer
	if err := yaml.Unmarshal(data, &composers); err != nil {
		log.Fatal(err)
	}

	inArray := func(a string, list []string) bool {
		for _, b := range list {
			if a == b {
				return true
			}
		}
		return false
	}

	var gotError = false
	for name, composer := range composers {
		if (len(names) == 0) || inArray(name, names) {
			if cid, err := composeContainer(ctx, name, composer); err != nil {
				log.Error(err)
				gotError = true
			} else {
				ctx.Println(cid)
			}
		}
	}
	if gotError {
		log.Fatal("Error: failed to compose one or more containers")
	}
}

func composeContainer(ctx *cobra.Command, name string, composer Composer) (string, error) {
	var (
		config     api.Config
		hostConfig api.HostConfig

		localVolumes   = make(map[string]struct{})
		bindVolumes    []string
		exposedPorts   = make(map[string]struct{})
		portBindings   = make(map[string][]api.PortBinding)
		links          []string
		deviceMappings []api.DeviceMapping
	)

	if composer.Image != "" {
		r, n, t, err := client.ParseRepositoryName(composer.Image)
		if err != nil {
			return "", err
		}
		composer.Image = n + ":" + t
		if r != "" {
			composer.Image = r + "/" + composer.Image
		}
	}

	docker, err := client.NewDockerClient(configPath, hostName, ctx.Out())
	if err != nil {
		return "", err
	}

	if composer.Build != "" {
		message, err := docker.BuildImage(composer.Build, composer.Image, false)
		if err != nil {
			return "", err
		}
		if composer.Image == "" {
			if _, err := fmt.Sscanf(message, "Successfully built %s", &composer.Image); err != nil {
				return "", err
			}
		}
	}

	for _, rawPort := range composer.Ports {
		var (
			hostPort, containerPort string
		)

		if !strings.Contains(rawPort, ":") {
			hostPort = ""
			containerPort = rawPort
		} else {
			parts := strings.Split(rawPort, ":")
			hostPort = parts[0]
			containerPort = parts[1]
		}

		port := fmt.Sprintf("%s/%s", containerPort, "tcp")
		if _, exists := exposedPorts[port]; !exists {
			exposedPorts[port] = struct{}{}
		}

		portBinding := api.PortBinding{
			HostPort: hostPort,
		}
		bslice, exists := portBindings[port]
		if !exists {
			bslice = []api.PortBinding{}
		}
		portBindings[port] = append(bslice, portBinding)
	}

	for _, containerPort := range composer.ExposedPorts {
		port := fmt.Sprintf("%s/%s", containerPort, "tcp")
		if _, exists := exposedPorts[port]; !exists {
			exposedPorts[port] = struct{}{}
		}
	}

	for _, volume := range composer.Volumes {
		if arr := strings.Split(volume, ":"); len(arr) > 1 {
			if arr[1] == "/" {
				return "", fmt.Errorf("Invalid bind mount: destination can't be '/'")
			}
			bindVolumes = append(bindVolumes, volume)
		} else if volume == "/" {
			return "", fmt.Errorf("Invalid volume: path can't be '/'")
		} else {
			localVolumes[volume] = struct{}{}
		}
	}

	for _, link := range append(composer.Links, composer.ExternalLinks...) {
		arr := strings.Split(link, ":")
		if len(arr) < 2 {
			links = append(links, arr[0]+":"+arr[0])
		} else {
			links = append(links, link)
		}
	}

	for _, device := range composer.Devices {
		src := ""
		dst := ""
		permissions := "rwm"
		arr := strings.Split(device, ":")
		switch len(arr) {
		case 3:
			permissions = arr[2]
			fallthrough
		case 2:
			dst = arr[1]
			fallthrough
		case 1:
			src = arr[0]
		default:
			return "", fmt.Errorf("Invalid device specification: %s", device)
		}

		if dst == "" {
			dst = src
		}

		deviceMapping := api.DeviceMapping{
			PathOnHost:        src,
			PathInContainer:   dst,
			CgroupPermissions: permissions,
		}
		deviceMappings = append(deviceMappings, deviceMapping)
	}

	parts := strings.Split(composer.RestartPolicy, ":")
	restartPolicy := api.RestartPolicy{}
	restartPolicy.Name = parts[0]
	if (restartPolicy.Name == "on-failure") && (len(parts) == 2) {
		count, err := strconv.Atoi(parts[1])
		if err != nil {
			return "", err
		}
		restartPolicy.MaximumRetryCount = count
	}

	config.Hostname = composer.Hostname
	config.Domainname = composer.Domainname
	config.User = composer.User
	config.Memory = composer.Memory
	config.MemorySwap = composer.MemorySwap
	config.CpuShares = composer.CpuShares
	config.Cpuset = composer.Cpuset
	config.ExposedPorts = exposedPorts
	config.Tty = composer.Tty
	config.OpenStdin = composer.OpenStdin
	config.Env = composer.Env
	config.Cmd = composer.Cmd
	config.Image = composer.Image
	config.Volumes = localVolumes
	config.WorkingDir = composer.WorkingDir
	if composer.Entrypoint != "" {
		config.Entrypoint = []string{composer.Entrypoint}
	}
	config.MacAddress = composer.MacAddress

	hostConfig.Binds = bindVolumes
	hostConfig.Privileged = composer.Privileged
	hostConfig.PortBindings = portBindings
	hostConfig.Links = links
	hostConfig.PublishAllPorts = composer.PublishAllPorts
	hostConfig.Dns = composer.Dns
	hostConfig.DnsSearch = composer.DnsSearch
	hostConfig.ExtraHosts = composer.ExtraHosts
	hostConfig.VolumesFrom = composer.VolumesFrom
	hostConfig.Devices = deviceMappings
	hostConfig.NetworkMode = composer.NetworkMode
	hostConfig.IpcMode = composer.IpcMode
	hostConfig.PidMode = composer.PidMode
	hostConfig.CapAdd = composer.CapAdd
	hostConfig.CapDrop = composer.CapDrop
	hostConfig.RestartPolicy = restartPolicy
	hostConfig.SecurityOpt = composer.SecurityOpt
	hostConfig.ReadonlyRootfs = composer.ReadonlyRootfs

	var cid string
	cid, err = docker.CreateContainer(name, config, hostConfig)
	if err != nil {
		if apiErr, ok := err.(api.Error); ok && (apiErr.StatusCode == 404) {
			if _, err := docker.PullImage(config.Image); err != nil {
				return "", err
			}

			cid, err = docker.CreateContainer(name, config, hostConfig)
			if err != nil {
				return "", err
			}
		} else {
			return "", err
		}
	}

	return cid, nil
}
<commit_msg>Build on the directory of compose.yaml<commit_after>package commands

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"

	log "github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
	"gopkg.in/yaml.v2"

	"github.com/ailispaw/talk2docker/api"
	"github.com/ailispaw/talk2docker/client"
)

var cmdCompose = &cobra.Command{
	Use:     "compose <PATH/TO/YAML> [NAME...]",
	Aliases: []string{"fig", "create"},
	Short:   "Compose containers",
	Long:    APP_NAME + " compose - Compose containers",
	Run:     composeContainers,
}

var cmdComposeContainers = &cobra.Command{
	Use:     "compose <PATH/TO/YAML> [NAME...]",
	Aliases: []string{"fig", "create"},
	Short:   "Compose containers",
	Long:    APP_NAME + " container compose - Compose containers",
	Run:     composeContainers,
}

func init() {
	cmdContainer.AddCommand(cmdComposeContainers)
}

type Composer struct {
	Build string `yaml:"build"`

	Ports   []string `yaml:"ports"`
	Volumes []string `yaml:"volumes"`

	// api.Config
	Hostname     string   `yaml:"hostname"`
	Domainname   string   `yaml:"domainname"`
	User         string   `yaml:"user"`
	Memory       int64    `yaml:"mem_limit"`
	MemorySwap   int64    `yaml:"mem_swap"`
	CpuShares    int64    `yaml:"cpu_shares"`
	Cpuset       string   `yaml:"cpuset"`
	ExposedPorts []string `yaml:"expose"`
	Tty          bool     `yaml:"tty"`
	OpenStdin    bool     `yaml:"stdin_open"`
	Env          []string `yaml:"environment"`
	Cmd          []string `yaml:"command"`
	Image        string   `yaml:"image"`
	WorkingDir   string   `yaml:"working_dir"`
	Entrypoint   string   `yaml:"entrypoint"`
	MacAddress   string   `yaml:"mac_address"`

	// api.HostConfig
	Privileged      bool     `yaml:"privileged"`
	Links           []string `yaml:"links"`
	ExternalLinks   []string `yaml:"external_links"`
	PublishAllPorts bool     `yaml:"publish_all"`
	Dns             []string `yaml:"dns"`
	DnsSearch       []string `yaml:"dns_search"`
	ExtraHosts      []string `yaml:"add_host"`
	VolumesFrom     []string `yaml:"volumes_from"`
	Devices         []string `yaml:"device"`
	NetworkMode     string   `yaml:"net"`
	IpcMode         string   `yaml:"ipc"`
	PidMode         string   `yaml:"pid"`
	CapAdd          []string `yaml:"cap_add"`
	CapDrop         []string `yaml:"cap_drop"`
	RestartPolicy   string   `yaml:"restart"`
	SecurityOpt     []string `yaml:"security_opt"`
	ReadonlyRootfs  bool     `yaml:"read_only"`
}

func composeContainers(ctx *cobra.Command, args []string) {
	if len(args) < 1 {
		ErrorExit(ctx, "Needs an argument <PATH/TO/YAML> to compose containers")
	}

	path := filepath.Clean(args[0])
	root := filepath.Dir(path)

	var names []string
	if len(args) > 1 {
		names = args[1:]
	}

	data, err := ioutil.ReadFile(path)
	if err != nil {
		log.Fatal(err)
	}

	os.Chdir(root)

	var composers map[string]Composer
	if err := yaml.Unmarshal(data, &composers); err != nil {
		log.Fatal(err)
	}

	inArray := func(a string, list []string) bool {
		for _, b := range list {
			if a == b {
				return true
			}
		}
		return false
	}

	var gotError = false
	for name, composer := range composers {
		if (len(names) == 0) || inArray(name, names) {
			if cid, err := composeContainer(ctx, name, composer); err != nil {
				log.Error(err)
				gotError = true
			} else {
				ctx.Println(cid)
			}
		}
	}
	if gotError {
		log.Fatal("Error: failed to compose one or more containers")
	}
}

func composeContainer(ctx *cobra.Command, name string, composer Composer) (string, error) {
	var (
		config     api.Config
		hostConfig api.HostConfig

		localVolumes   = make(map[string]struct{})
		bindVolumes    []string
		exposedPorts   = make(map[string]struct{})
		portBindings   = make(map[string][]api.PortBinding)
		links          []string
		deviceMappings []api.DeviceMapping
	)

	if composer.Image != "" {
		r, n, t, err := client.ParseRepositoryName(composer.Image)
		if err != nil {
			return "", err
		}
		composer.Image = n + ":" + t
		if r != "" {
			composer.Image = r + "/" + composer.Image
		}
	}

	docker, err := client.NewDockerClient(configPath, hostName, ctx.Out())
	if err != nil {
		return "", err
	}

	if composer.Build != "" {
		message, err := docker.BuildImage(composer.Build, composer.Image, false)
		if err != nil {
			return "", err
		}
		if composer.Image == "" {
			if _, err := fmt.Sscanf(message, "Successfully built %s", &composer.Image); err != nil {
				return "", err
			}
		}
	}

	for _, rawPort := range composer.Ports {
		var (
			hostPort, containerPort string
		)

		if !strings.Contains(rawPort, ":") {
			hostPort = ""
			containerPort = rawPort
		} else {
			parts := strings.Split(rawPort, ":")
			hostPort = parts[0]
			containerPort = parts[1]
		}

		port := fmt.Sprintf("%s/%s", containerPort, "tcp")
		if _, exists := exposedPorts[port]; !exists {
			exposedPorts[port] = struct{}{}
		}

		portBinding := api.PortBinding{
			HostPort: hostPort,
		}
		bslice, exists := portBindings[port]
		if !exists {
			bslice = []api.PortBinding{}
		}
		portBindings[port] = append(bslice, portBinding)
	}

	for _, containerPort := range composer.ExposedPorts {
		port := fmt.Sprintf("%s/%s", containerPort, "tcp")
		if _, exists := exposedPorts[port]; !exists {
			exposedPorts[port] = struct{}{}
		}
	}

	for _, volume := range composer.Volumes {
		if arr := strings.Split(volume, ":"); len(arr) > 1 {
			if arr[1] == "/" {
				return "", fmt.Errorf("Invalid bind mount: destination can't be '/'")
			}
			bindVolumes = append(bindVolumes, volume)
		} else if volume == "/" {
			return "", fmt.Errorf("Invalid volume: path can't be '/'")
		} else {
			localVolumes[volume] = struct{}{}
		}
	}

	for _, link := range append(composer.Links, composer.ExternalLinks...) {
		arr := strings.Split(link, ":")
		if len(arr) < 2 {
			links = append(links, arr[0]+":"+arr[0])
		} else {
			links = append(links, link)
		}
	}

	for _, device := range composer.Devices {
		src := ""
		dst := ""
		permissions := "rwm"
		arr := strings.Split(device, ":")
		switch len(arr) {
		case 3:
			permissions = arr[2]
			fallthrough
		case 2:
			dst = arr[1]
			fallthrough
		case 1:
			src = arr[0]
		default:
			return "", fmt.Errorf("Invalid device specification: %s", device)
		}

		if dst == "" {
			dst = src
		}

		deviceMapping := api.DeviceMapping{
			PathOnHost:        src,
			PathInContainer:   dst,
			CgroupPermissions: permissions,
		}
		deviceMappings = append(deviceMappings, deviceMapping)
	}

	parts := strings.Split(composer.RestartPolicy, ":")
	restartPolicy := api.RestartPolicy{}
	restartPolicy.Name = parts[0]
	if (restartPolicy.Name == "on-failure") && (len(parts) == 2) {
		count, err := strconv.Atoi(parts[1])
		if err != nil {
			return "", err
		}
		restartPolicy.MaximumRetryCount = count
	}

	config.Hostname = composer.Hostname
	config.Domainname = composer.Domainname
	config.User = composer.User
	config.Memory = composer.Memory
	config.MemorySwap = composer.MemorySwap
	config.CpuShares = composer.CpuShares
	config.Cpuset = composer.Cpuset
	config.ExposedPorts = exposedPorts
	config.Tty = composer.Tty
	config.OpenStdin = composer.OpenStdin
	config.Env = composer.Env
	config.Cmd = composer.Cmd
	config.Image = composer.Image
	config.Volumes = localVolumes
	config.WorkingDir = composer.WorkingDir
	if composer.Entrypoint != "" {
		config.Entrypoint = []string{composer.Entrypoint}
	}
	config.MacAddress = composer.MacAddress

	hostConfig.Binds = bindVolumes
	hostConfig.Privileged = composer.Privileged
	hostConfig.PortBindings = portBindings
	hostConfig.Links = links
	hostConfig.PublishAllPorts = composer.PublishAllPorts
	hostConfig.Dns = composer.Dns
	hostConfig.DnsSearch = composer.DnsSearch
	hostConfig.ExtraHosts = composer.ExtraHosts
	hostConfig.VolumesFrom = composer.VolumesFrom
	hostConfig.Devices = deviceMappings
	hostConfig.NetworkMode = composer.NetworkMode
	hostConfig.IpcMode = composer.IpcMode
	hostConfig.PidMode = composer.PidMode
	hostConfig.CapAdd = composer.CapAdd
	hostConfig.CapDrop = composer.CapDrop
	hostConfig.RestartPolicy = restartPolicy
	hostConfig.SecurityOpt = composer.SecurityOpt
	hostConfig.ReadonlyRootfs = composer.ReadonlyRootfs

	var cid string
	cid, err = docker.CreateContainer(name, config, hostConfig)
	if err != nil {
		if apiErr, ok := err.(api.Error); ok && (apiErr.StatusCode == 404) {
			if _, err := docker.PullImage(config.Image); err != nil {
				return "", err
			}

			cid, err = docker.CreateContainer(name, config, hostConfig)
			if err != nil {
				return "", err
			}
		} else {
			return "", err
		}
	}

	return cid, nil
}
<|endoftext|>


<commit_before>package commands

import (
	"github.com/spf13/cobra"

	"github.com/nanobox-io/nanobox/commands/steps"
	"github.com/nanobox-io/nanobox/processors"
	"github.com/nanobox-io/nanobox/util/display"
)

var (

	// ImplodeCmd ...
	ImplodeCmd = &cobra.Command{
		Use:    "implode",
		Short:  "remove all nanobox created files and vms",
		Long:   ``,
		PreRun: steps.Run("start"),
		Run:    implodeFn,
	}
)

// implodeFn ...
func implodeFn(ccmd *cobra.Command, args []string) {
	display.CommandErr(processors.Implode())
}
<commit_msg>Update implode.go<commit_after>package commands

import (
	"github.com/spf13/cobra"

	"github.com/nanobox-io/nanobox/commands/steps"
	"github.com/nanobox-io/nanobox/processors"
	"github.com/nanobox-io/nanobox/util/display"
)

var (

	// ImplodeCmd ...
	ImplodeCmd = &cobra.Command{
		Use:    "implode",
		Short:  "Removes all Nanobox-created containers, files, & data",
		Long:   `
Removes the Nanobox container, all projects, filesystem mounts,
& local data. All that will remain is nanobox binaries.
		`,
		PreRun: steps.Run("start"),
		Run:    implodeFn,
	}
)

// implodeFn ...
func implodeFn(ccmd *cobra.Command, args []string) {
	display.CommandErr(processors.Implode())
}
<|endoftext|>