<commit_before>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package all

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"os"
	"reflect"

	"github.com/juju/errors"
	"github.com/juju/names"
	"gopkg.in/juju/charm.v6-unstable"
	charmresource "gopkg.in/juju/charm.v6-unstable/resource"
	"gopkg.in/juju/charmrepo.v2-unstable"

	jujucmd "github.com/juju/cmd"
	"github.com/juju/juju/api"
	"github.com/juju/juju/api/base"
	"github.com/juju/juju/apiserver/common"
	"github.com/juju/juju/cmd/envcmd"
	"github.com/juju/juju/cmd/juju/commands"
	"github.com/juju/juju/resource"
	"github.com/juju/juju/resource/api/client"
	internalclient "github.com/juju/juju/resource/api/private/client"
	internalserver "github.com/juju/juju/resource/api/private/server"
	"github.com/juju/juju/resource/api/server"
	"github.com/juju/juju/resource/cmd"
	"github.com/juju/juju/resource/context"
	contextcmd "github.com/juju/juju/resource/context/cmd"
	"github.com/juju/juju/resource/persistence"
	"github.com/juju/juju/resource/state"
	corestate "github.com/juju/juju/state"
	unitercontext "github.com/juju/juju/worker/uniter/runner/context"
	"github.com/juju/juju/worker/uniter/runner/jujuc"
)

// resources exposes the registration methods needed
// for the top-level component machinery.
type resources struct{}

// RegisterForServer is the top-level registration method
// for the component in a jujud context.
func (r resources) registerForServer() error {
	r.registerState()
	r.registerPublicFacade()
	r.registerHookContext()
	return nil
}

// RegisterForClient is the top-level registration method
// for the component in a "juju" command context.
func (r resources) registerForClient() error {
	r.registerPublicCommands()
	return nil
}

// registerPublicFacade adds the resources public API facade
// to the API server.
func (r resources) registerPublicFacade() {
	if !markRegistered(resource.ComponentName, "public-facade") {
		return
	}

	common.RegisterStandardFacade(
		resource.ComponentName,
		server.Version,
		r.newPublicFacade,
	)
}

// newPublicFacade is passed into common.RegisterStandardFacade
// in registerPublicFacade.
func (resources) newPublicFacade(st *corestate.State, _ *common.Resources, authorizer common.Authorizer) (*server.Facade, error) {
	if !authorizer.AuthClient() {
		return nil, common.ErrPerm
	}

	rst, err := st.Resources()
	//rst, err := state.NewState(&resourceState{raw: st})
	if err != nil {
		return nil, errors.Trace(err)
	}

	return server.NewFacade(rst), nil
}

// resourcesApiClient adds a Close() method to the resources public API client.
type resourcesAPIClient struct {
	*client.Client
	closeConnFunc func() error
}

// Close implements io.Closer.
func (client resourcesAPIClient) Close() error {
	return client.closeConnFunc()
}

// registerState registers the state functionality for resources.
func (resources) registerState() {
	if !markRegistered(resource.ComponentName, "state") {
		return
	}

	newResources := func(persist corestate.Persistence) corestate.Resources {
		st := state.NewState(&resourceState{persist: persist})
		return st
	}

	corestate.SetResourcesComponent(newResources)

	corestate.AddServicePostFuncs["resources"] = saveResourcesForDemo
}

// resourceState is a wrapper around state.State that supports the needs
// of resources.
type resourceState struct {
	persist corestate.Persistence
}

// Persistence implements resource/state.RawState.
func (st resourceState) Persistence() state.Persistence {
	return persistence.NewPersistence(st.persist)
}

// Storage implements resource/state.RawState.
func (st resourceState) Storage() state.Storage {
	return st.persist.NewStorage()
}

// registerPublicCommands adds the resources-related commands
// to the "juju" supercommand.
func (r resources) registerPublicCommands() {
	if !markRegistered(resource.ComponentName, "public-commands") {
		return
	}

	newShowAPIClient := func(command *cmd.ShowCommand) (cmd.CharmResourceLister, error) {
		client := newCharmstoreClient()
		return &charmstoreClient{client}, nil
	}
	commands.RegisterEnvCommand(func() envcmd.EnvironCommand {
		return cmd.NewShowCommand(newShowAPIClient)
	})

	commands.RegisterEnvCommand(func() envcmd.EnvironCommand {
		return cmd.NewUploadCommand(cmd.UploadDeps{
			NewClient: func(c *cmd.UploadCommand) (cmd.UploadClient, error) {
				return r.newClient(c.NewAPIRoot)
			},
			OpenResource: func(s string) (cmd.ReadSeekCloser, error) {
				return os.Open(s)
			},
		})

	})

	commands.RegisterEnvCommand(func() envcmd.EnvironCommand {
		return cmd.NewShowServiceCommand(cmd.ShowServiceDeps{
			NewClient: func(c *cmd.ShowServiceCommand) (cmd.ShowServiceClient, error) {
				return r.newClient(c.NewAPIRoot)
			},
		})
	})
}

func newCharmstoreClient() charmrepo.Interface {
	// Also see apiserver/service/charmstore.go.
	var args charmrepo.NewCharmStoreParams
	client := charmrepo.NewCharmStore(args)
	return client
}

// TODO(ericsnow) Get rid of charmstoreClient one charmrepo.Interface grows the methods.

type charmstoreClient struct {
	charmrepo.Interface
}

func (charmstoreClient) ListResources(charmURLs []charm.URL) ([][]charmresource.Resource, error) {
	// TODO(natefinch): this is all demo stuff and should go away afterward.
	if len(charmURLs) != 1 || charmURLs[0].Name != "starsay" {
		res := make([][]charmresource.Resource, len(charmURLs))
		return res, nil
	}
	var fingerprint = []byte("123456789012345678901234567890123456789012345678")
	fp, err := charmresource.NewFingerprint(fingerprint)
	if err != nil {
		return nil, err
	}
	res := [][]charmresource.Resource{
		{
			{
				Meta: charmresource.Meta{
					Name:    "store-resource",
					Type:    charmresource.TypeFile,
					Path:    "filename.tgz",
					Comment: "One line that is useful when operators need to push it.",
				},
				Origin:      charmresource.OriginStore,
				Revision:    1,
				Fingerprint: fp,
				Size:        1,
			},
			{
				Meta: charmresource.Meta{
					Name:    "upload-resource",
					Type:    charmresource.TypeFile,
					Path:    "somename.xml",
					Comment: "Who uses xml anymore?",
				},
				Origin: charmresource.OriginUpload,
			},
		},
	}
	return res, nil
}

func (charmstoreClient) Close() error {
	return nil
}

type apicommand interface {
	NewAPIRoot() (api.Connection, error)
}

func (resources) newClient(newAPICaller func() (api.Connection, error)) (*client.Client, error) {
	apiCaller, err := newAPICaller()
	if err != nil {
		return nil, errors.Trace(err)
	}
	caller := base.NewFacadeCallerForVersion(apiCaller, resource.ComponentName, server.Version)
	doer, err := apiCaller.HTTPClient()
	if err != nil {
		return nil, errors.Trace(err)
	}

	// The apiCaller takes care of prepending /environment/<envUUID>.
	cl := client.NewClient(caller, doer, apiCaller)
	return cl, nil
}

// TODO(natefinch) DEMO CODE, revisit after demo!
func saveResourcesForDemo(st *corestate.State, args corestate.AddServiceArgs) error {
	resourceState, err := st.Resources()
	if err != nil {
		return errors.Annotate(err, "can't get resources from state")
	}

	for _, meta := range args.Charm.Meta().Resources {
		res := resource.Resource{
			Resource: charmresource.Resource{
				Meta: meta,
				// TODO(natefinch): how do we determine this at deploy time?
				Origin: charmresource.OriginUpload,
			},
		}

		// no data for you!
		r := &bytes.Buffer{}

		if err := resourceState.SetResource(args.Name, res, r); err != nil {
			return errors.Annotatef(err, "can't add resource %q", meta.Name)
		}
	}
	return nil
}

// TODO(katco): This seems to be common across components. Pop up a
// level and genericize?
func (r resources) registerHookContext() {
	if markRegistered(resource.ComponentName, "hook-context") == false {
		return
	}

	unitercontext.RegisterComponentFunc(
		resource.ComponentName,
		func(config unitercontext.ComponentConfig) (jujuc.ContextComponent, error) {
			unitID := names.NewUnitTag(config.UnitName).String()
			hctxClient, err := r.newUnitFacadeClient(unitID, config.APICaller)
			if err != nil {
				return nil, errors.Trace(err)
			}
			// TODO(ericsnow) Pass the unit's tag through to the component?
			return context.NewContextAPI(hctxClient, config.DataDir), nil
		},
	)

	r.registerHookContextCommands()
	r.registerHookContextFacade()
}

func (c resources) registerHookContextCommands() {
	if markRegistered(resource.ComponentName, "hook-context-commands") == false {
		return
	}

	jujuc.RegisterCommand(
		contextcmd.GetCmdName,
		func(ctx jujuc.Context) (jujucmd.Command, error) {
			compCtx, err := ctx.Component(resource.ComponentName)
			if err != nil {
				return nil, errors.Trace(err)
			}
			// TODO(katco): Check type assertion; don't panic
			typedCtx, ok := compCtx.(*context.Context)
			if !ok {
				return nil, errors.Trace(err)
			}
			cmd, err := contextcmd.NewGetCmd(typedCtx)
			if err != nil {
				return nil, errors.Trace(err)
			}
			return cmd, nil
		},
	)
}

func (r resources) registerHookContextFacade() {
	common.RegisterHookContextFacade(
		context.HookContextFacade,
		internalserver.FacadeVersion,
		r.newHookContextFacade,
		reflect.TypeOf(&internalserver.UnitFacade{}),
	)
}

// resourcesUnitDatastore is a shim to elide serviceName from
// ListResources.
type resourcesUnitDataStore struct {
	resources   corestate.Resources
	serviceName string
}

// ListResources implements resource/api/private/server.UnitDataStore.
func (ds *resourcesUnitDataStore) ListResources() ([]resource.Resource, error) {
	return ds.resources.ListResources(ds.serviceName)
}

// OpenResource implements resource/api/private/server.UnitDataStore.
func (ds *resourcesUnitDataStore) OpenResource(name string) (resource.Resource, io.ReadCloser, error) {
	return ds.resources.OpenResource(ds.serviceName, name)
}

func (r resources) newHookContextFacade(st *corestate.State, unit *corestate.Unit) (interface{}, error) {
	res, err := st.Resources()
	if err != nil {
		return nil, errors.Trace(err)
	}
	return internalserver.NewUnitFacade(&resourcesUnitDataStore{res, unit.ServiceName()}), nil
}

type UnitDoer struct {
	doer     internalclient.UnitDoer
	unitName string
}

// Do implements httprequest.Doer.
func (d *UnitDoer) Do(req *http.Request, body io.ReadSeeker, response interface{}) error {
	req.URL.Path = fmt.Sprintf("/units/%s%s", d.unitName, req.URL.Path)
	return d.doer.Do(req, body, response)
}

func (r resources) newUnitFacadeClient(unitName string, caller base.APICaller) (context.APIClient, error) {

	facadeCaller := base.NewFacadeCallerForVersion(caller, context.HookContextFacade, 1)
	doer, err := caller.HTTPClient()
	if err != nil {
		return nil, errors.Trace(err)
	}

	unitDoer := &UnitDoer{
		doer:     doer,
		unitName: unitName,
	}

	return internalclient.NewUnitFacadeClient(facadeCaller, unitDoer), nil
}
<commit_msg>Replace a magic number with a constant.<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package all

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
	"os"
	"reflect"

	"github.com/juju/errors"
	"github.com/juju/names"
	"gopkg.in/juju/charm.v6-unstable"
	charmresource "gopkg.in/juju/charm.v6-unstable/resource"
	"gopkg.in/juju/charmrepo.v2-unstable"

	jujucmd "github.com/juju/cmd"
	"github.com/juju/juju/api"
	"github.com/juju/juju/api/base"
	"github.com/juju/juju/apiserver/common"
	"github.com/juju/juju/cmd/envcmd"
	"github.com/juju/juju/cmd/juju/commands"
	"github.com/juju/juju/resource"
	"github.com/juju/juju/resource/api/client"
	internalclient "github.com/juju/juju/resource/api/private/client"
	internalserver "github.com/juju/juju/resource/api/private/server"
	"github.com/juju/juju/resource/api/server"
	"github.com/juju/juju/resource/cmd"
	"github.com/juju/juju/resource/context"
	contextcmd "github.com/juju/juju/resource/context/cmd"
	"github.com/juju/juju/resource/persistence"
	"github.com/juju/juju/resource/state"
	corestate "github.com/juju/juju/state"
	unitercontext "github.com/juju/juju/worker/uniter/runner/context"
	"github.com/juju/juju/worker/uniter/runner/jujuc"
)

// resources exposes the registration methods needed
// for the top-level component machinery.
type resources struct{}

// RegisterForServer is the top-level registration method
// for the component in a jujud context.
func (r resources) registerForServer() error {
	r.registerState()
	r.registerPublicFacade()
	r.registerHookContext()
	return nil
}

// RegisterForClient is the top-level registration method
// for the component in a "juju" command context.
func (r resources) registerForClient() error {
	r.registerPublicCommands()
	return nil
}

// registerPublicFacade adds the resources public API facade
// to the API server.
func (r resources) registerPublicFacade() {
	if !markRegistered(resource.ComponentName, "public-facade") {
		return
	}

	common.RegisterStandardFacade(
		resource.ComponentName,
		server.Version,
		r.newPublicFacade,
	)
}

// newPublicFacade is passed into common.RegisterStandardFacade
// in registerPublicFacade.
func (resources) newPublicFacade(st *corestate.State, _ *common.Resources, authorizer common.Authorizer) (*server.Facade, error) {
	if !authorizer.AuthClient() {
		return nil, common.ErrPerm
	}

	rst, err := st.Resources()
	//rst, err := state.NewState(&resourceState{raw: st})
	if err != nil {
		return nil, errors.Trace(err)
	}

	return server.NewFacade(rst), nil
}

// resourcesApiClient adds a Close() method to the resources public API client.
type resourcesAPIClient struct {
	*client.Client
	closeConnFunc func() error
}

// Close implements io.Closer.
func (client resourcesAPIClient) Close() error {
	return client.closeConnFunc()
}

// registerState registers the state functionality for resources.
func (resources) registerState() {
	if !markRegistered(resource.ComponentName, "state") {
		return
	}

	newResources := func(persist corestate.Persistence) corestate.Resources {
		st := state.NewState(&resourceState{persist: persist})
		return st
	}

	corestate.SetResourcesComponent(newResources)

	corestate.AddServicePostFuncs["resources"] = saveResourcesForDemo
}

// resourceState is a wrapper around state.State that supports the needs
// of resources.
type resourceState struct {
	persist corestate.Persistence
}

// Persistence implements resource/state.RawState.
func (st resourceState) Persistence() state.Persistence {
	return persistence.NewPersistence(st.persist)
}

// Storage implements resource/state.RawState.
func (st resourceState) Storage() state.Storage {
	return st.persist.NewStorage()
}

// registerPublicCommands adds the resources-related commands
// to the "juju" supercommand.
func (r resources) registerPublicCommands() {
	if !markRegistered(resource.ComponentName, "public-commands") {
		return
	}

	newShowAPIClient := func(command *cmd.ShowCommand) (cmd.CharmResourceLister, error) {
		client := newCharmstoreClient()
		return &charmstoreClient{client}, nil
	}
	commands.RegisterEnvCommand(func() envcmd.EnvironCommand {
		return cmd.NewShowCommand(newShowAPIClient)
	})

	commands.RegisterEnvCommand(func() envcmd.EnvironCommand {
		return cmd.NewUploadCommand(cmd.UploadDeps{
			NewClient: func(c *cmd.UploadCommand) (cmd.UploadClient, error) {
				return r.newClient(c.NewAPIRoot)
			},
			OpenResource: func(s string) (cmd.ReadSeekCloser, error) {
				return os.Open(s)
			},
		})

	})

	commands.RegisterEnvCommand(func() envcmd.EnvironCommand {
		return cmd.NewShowServiceCommand(cmd.ShowServiceDeps{
			NewClient: func(c *cmd.ShowServiceCommand) (cmd.ShowServiceClient, error) {
				return r.newClient(c.NewAPIRoot)
			},
		})
	})
}

func newCharmstoreClient() charmrepo.Interface {
	// Also see apiserver/service/charmstore.go.
	var args charmrepo.NewCharmStoreParams
	client := charmrepo.NewCharmStore(args)
	return client
}

// TODO(ericsnow) Get rid of charmstoreClient one charmrepo.Interface grows the methods.

type charmstoreClient struct {
	charmrepo.Interface
}

func (charmstoreClient) ListResources(charmURLs []charm.URL) ([][]charmresource.Resource, error) {
	// TODO(natefinch): this is all demo stuff and should go away afterward.
	if len(charmURLs) != 1 || charmURLs[0].Name != "starsay" {
		res := make([][]charmresource.Resource, len(charmURLs))
		return res, nil
	}
	var fingerprint = []byte("123456789012345678901234567890123456789012345678")
	fp, err := charmresource.NewFingerprint(fingerprint)
	if err != nil {
		return nil, err
	}
	res := [][]charmresource.Resource{
		{
			{
				Meta: charmresource.Meta{
					Name:    "store-resource",
					Type:    charmresource.TypeFile,
					Path:    "filename.tgz",
					Comment: "One line that is useful when operators need to push it.",
				},
				Origin:      charmresource.OriginStore,
				Revision:    1,
				Fingerprint: fp,
				Size:        1,
			},
			{
				Meta: charmresource.Meta{
					Name:    "upload-resource",
					Type:    charmresource.TypeFile,
					Path:    "somename.xml",
					Comment: "Who uses xml anymore?",
				},
				Origin: charmresource.OriginUpload,
			},
		},
	}
	return res, nil
}

func (charmstoreClient) Close() error {
	return nil
}

type apicommand interface {
	NewAPIRoot() (api.Connection, error)
}

func (resources) newClient(newAPICaller func() (api.Connection, error)) (*client.Client, error) {
	apiCaller, err := newAPICaller()
	if err != nil {
		return nil, errors.Trace(err)
	}
	caller := base.NewFacadeCallerForVersion(apiCaller, resource.ComponentName, server.Version)
	doer, err := apiCaller.HTTPClient()
	if err != nil {
		return nil, errors.Trace(err)
	}

	// The apiCaller takes care of prepending /environment/<envUUID>.
	cl := client.NewClient(caller, doer, apiCaller)
	return cl, nil
}

// TODO(natefinch) DEMO CODE, revisit after demo!
func saveResourcesForDemo(st *corestate.State, args corestate.AddServiceArgs) error {
	resourceState, err := st.Resources()
	if err != nil {
		return errors.Annotate(err, "can't get resources from state")
	}

	for _, meta := range args.Charm.Meta().Resources {
		res := resource.Resource{
			Resource: charmresource.Resource{
				Meta: meta,
				// TODO(natefinch): how do we determine this at deploy time?
				Origin: charmresource.OriginUpload,
			},
		}

		// no data for you!
		r := &bytes.Buffer{}

		if err := resourceState.SetResource(args.Name, res, r); err != nil {
			return errors.Annotatef(err, "can't add resource %q", meta.Name)
		}
	}
	return nil
}

// TODO(katco): This seems to be common across components. Pop up a
// level and genericize?
func (r resources) registerHookContext() {
	if markRegistered(resource.ComponentName, "hook-context") == false {
		return
	}

	unitercontext.RegisterComponentFunc(
		resource.ComponentName,
		func(config unitercontext.ComponentConfig) (jujuc.ContextComponent, error) {
			unitID := names.NewUnitTag(config.UnitName).String()
			hctxClient, err := r.newUnitFacadeClient(unitID, config.APICaller)
			if err != nil {
				return nil, errors.Trace(err)
			}
			// TODO(ericsnow) Pass the unit's tag through to the component?
			return context.NewContextAPI(hctxClient, config.DataDir), nil
		},
	)

	r.registerHookContextCommands()
	r.registerHookContextFacade()
}

func (c resources) registerHookContextCommands() {
	if markRegistered(resource.ComponentName, "hook-context-commands") == false {
		return
	}

	jujuc.RegisterCommand(
		contextcmd.GetCmdName,
		func(ctx jujuc.Context) (jujucmd.Command, error) {
			compCtx, err := ctx.Component(resource.ComponentName)
			if err != nil {
				return nil, errors.Trace(err)
			}
			// TODO(katco): Check type assertion; don't panic
			typedCtx, ok := compCtx.(*context.Context)
			if !ok {
				return nil, errors.Trace(err)
			}
			cmd, err := contextcmd.NewGetCmd(typedCtx)
			if err != nil {
				return nil, errors.Trace(err)
			}
			return cmd, nil
		},
	)
}

func (r resources) registerHookContextFacade() {
	common.RegisterHookContextFacade(
		context.HookContextFacade,
		internalserver.FacadeVersion,
		r.newHookContextFacade,
		reflect.TypeOf(&internalserver.UnitFacade{}),
	)
}

// resourcesUnitDatastore is a shim to elide serviceName from
// ListResources.
type resourcesUnitDataStore struct {
	resources   corestate.Resources
	serviceName string
}

// ListResources implements resource/api/private/server.UnitDataStore.
func (ds *resourcesUnitDataStore) ListResources() ([]resource.Resource, error) {
	return ds.resources.ListResources(ds.serviceName)
}

// OpenResource implements resource/api/private/server.UnitDataStore.
func (ds *resourcesUnitDataStore) OpenResource(name string) (resource.Resource, io.ReadCloser, error) {
	return ds.resources.OpenResource(ds.serviceName, name)
}

func (r resources) newHookContextFacade(st *corestate.State, unit *corestate.Unit) (interface{}, error) {
	res, err := st.Resources()
	if err != nil {
		return nil, errors.Trace(err)
	}
	return internalserver.NewUnitFacade(&resourcesUnitDataStore{res, unit.ServiceName()}), nil
}

type UnitDoer struct {
	doer     internalclient.UnitDoer
	unitName string
}

// Do implements httprequest.Doer.
func (d *UnitDoer) Do(req *http.Request, body io.ReadSeeker, response interface{}) error {
	req.URL.Path = fmt.Sprintf("/units/%s%s", d.unitName, req.URL.Path)
	return d.doer.Do(req, body, response)
}

func (r resources) newUnitFacadeClient(unitName string, caller base.APICaller) (context.APIClient, error) {

	facadeCaller := base.NewFacadeCallerForVersion(caller, context.HookContextFacade, internalserver.FacadeVersion)
	doer, err := caller.HTTPClient()
	if err != nil {
		return nil, errors.Trace(err)
	}

	unitDoer := &UnitDoer{
		doer:     doer,
		unitName: unitName,
	}

	return internalclient.NewUnitFacadeClient(facadeCaller, unitDoer), nil
}
<|endoftext|>


<commit_before>package stripe

import "encoding/json"

// WebhookEndpointParams is the set of parameters that can be used when creating a webhook endpoint.
// For more details see https://stripe.com/docs/api#create_webhook_endpoint.
type WebhookEndpointParams struct {
	Params        `form:"*"`
	Connect       *bool     `form:"connect"`
	Disabled      *bool     `form:"disabled"`
	EnabledEvents []*string `form:"enabled_events"`
	URL           *string   `form:"url"`

	// This parameter is only available on creation.
	// We recommend setting the API version that the library is pinned to. See apiversion in stripe.go
	APIVersion *string `form:"api_version"`
}

// WebhookEndpointListParams is the set of parameters that can be used when listing webhook endpoints.
// For more detail see https://stripe.com/docs/api#list_webhook_endpoints.
type WebhookEndpointListParams struct {
	ListParams   `form:"*"`
	Created      *int64            `form:"created"`
	CreatedRange *RangeQueryParams `form:"created"`
}

// WebhookEndpoint is the resource representing a Stripe webhook endpoint.
// For more details see https://stripe.com/docs/api#webhook_endpoints.
type WebhookEndpoint struct {
	APIVersion    string   `json:"api_version"`
	Application   string   `json:"application"`
	Connect       bool     `json:"connect"`
	Created       int64    `json:"created"`
	Deleted       bool     `json:"deleted"`
	EnabledEvents []string `json:"enabled_events"`
	ID            string   `json:"id"`
	Livemode      bool     `json:"livemode"`
	Object        string   `json:"object"`
	Secret        string   `json:"secret"`
	Status        string   `json:"status"`
	URL           string   `json:"url"`
}

// WebhookEndpointList is a list of webhook endpoints as retrieved from a list endpoint.
type WebhookEndpointList struct {
	ListMeta
	Data []*WebhookEndpoint `json:"data"`
}

// UnmarshalJSON handles deserialization of a WebhookEndpoint.
// This custom unmarshaling is needed because the resulting
// property may be an id or the full struct if it was expanded.
func (c *WebhookEndpoint) UnmarshalJSON(data []byte) error {
	if id, ok := ParseID(data); ok {
		c.ID = id
		return nil
	}

	type endpoint WebhookEndpoint
	var v endpoint
	if err := json.Unmarshal(data, &v); err != nil {
		return err
	}

	*c = WebhookEndpoint(v)
	return nil
}
<commit_msg>Add `Description` on `WebhookEndpoint`<commit_after>package stripe

import "encoding/json"

// WebhookEndpointParams is the set of parameters that can be used when creating a webhook endpoint.
// For more details see https://stripe.com/docs/api#create_webhook_endpoint.
type WebhookEndpointParams struct {
	Params        `form:"*"`
	Connect       *bool     `form:"connect"`
	Description   *string   `form:"description"`
	Disabled      *bool     `form:"disabled"`
	EnabledEvents []*string `form:"enabled_events"`
	URL           *string   `form:"url"`

	// This parameter is only available on creation.
	// We recommend setting the API version that the library is pinned to. See apiversion in stripe.go
	APIVersion *string `form:"api_version"`
}

// WebhookEndpointListParams is the set of parameters that can be used when listing webhook endpoints.
// For more detail see https://stripe.com/docs/api#list_webhook_endpoints.
type WebhookEndpointListParams struct {
	ListParams   `form:"*"`
	Created      *int64            `form:"created"`
	CreatedRange *RangeQueryParams `form:"created"`
}

// WebhookEndpoint is the resource representing a Stripe webhook endpoint.
// For more details see https://stripe.com/docs/api#webhook_endpoints.
type WebhookEndpoint struct {
	APIVersion    string   `json:"api_version"`
	Application   string   `json:"application"`
	Connect       bool     `json:"connect"`
	Created       int64    `json:"created"`
	Deleted       bool     `json:"deleted"`
	Description   string   `json:"description"`
	EnabledEvents []string `json:"enabled_events"`
	ID            string   `json:"id"`
	Livemode      bool     `json:"livemode"`
	Object        string   `json:"object"`
	Secret        string   `json:"secret"`
	Status        string   `json:"status"`
	URL           string   `json:"url"`
}

// WebhookEndpointList is a list of webhook endpoints as retrieved from a list endpoint.
type WebhookEndpointList struct {
	ListMeta
	Data []*WebhookEndpoint `json:"data"`
}

// UnmarshalJSON handles deserialization of a WebhookEndpoint.
// This custom unmarshaling is needed because the resulting
// property may be an id or the full struct if it was expanded.
func (c *WebhookEndpoint) UnmarshalJSON(data []byte) error {
	if id, ok := ParseID(data); ok {
		c.ID = id
		return nil
	}

	type endpoint WebhookEndpoint
	var v endpoint
	if err := json.Unmarshal(data, &v); err != nil {
		return err
	}

	*c = WebhookEndpoint(v)
	return nil
}
<|endoftext|>


<commit_before>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package nat

import (
	"errors"
	"net"
	"sync"
	"time"

	"github.com/ava-labs/gecko/utils/logging"
)

const (
	mapTimeout       = 30 * time.Minute
	mapUpdateTimeout = mapTimeout / 2
	maxRetries       = 20
)

// Router describes the functionality that a network device must support to be
// able to open ports to an external IP.
type Router interface {
	MapPort(protocol string, intPort, extPort uint16, desc string, duration time.Duration) error
	UnmapPort(protocol string, intPort, extPort uint16) error
	ExternalIP() (net.IP, error)
	GetPortMappingEntry(extPort uint16, protocol string) (
		InternalIP string,
		InternalPort uint16,
		Description string,
		err error,
	)
}

// GetRouter returns a router on the current network.
func GetRouter() Router {
	if r := getUPnPRouter(); r != nil {
		return r
	}
	if r := getPMPRouter(); r != nil {
		return r
	}

	return NewNoRouter()
}

// Mapper attempts to open a set of ports on a router
type Mapper struct {
	log    logging.Logger
	r      Router
	closer chan struct{}
	wg     sync.WaitGroup
}

// NewPortMapper returns an initialized mapper
func NewPortMapper(log logging.Logger, r Router) Mapper {
	return Mapper{
		log:    log,
		r:      r,
		closer: make(chan struct{}),
	}
}

// Map sets up port mapping using given protocol, internal and external ports
// and returns the final port mapped. It returns 0 if mapping failed after the
// maximun number of retries
func (dev *Mapper) Map(protocol string, intPort uint16, desc string) (uint16, error) {
	mappedPort := make(chan uint16)

	go dev.keepPortMapping(mappedPort, protocol, intPort, desc)

	port := <-mappedPort
	if port == 0 {
		return 0, errors.New("failed to map port")
	}
	return port, nil
}

// keepPortMapping runs in the background to keep a port mapped. It renews the
// the port mapping in mapUpdateTimeout.
func (dev *Mapper) keepPortMapping(mappedPort chan<- uint16, protocol string,
	intPort uint16, desc string) {
	updateTimer := time.NewTimer(mapUpdateTimeout)

	for i := 0; i <= maxRetries; i++ {
		extPort := intPort + uint16(i)
		if intaddr, intPort, desc, err := dev.r.GetPortMappingEntry(extPort, protocol); err == nil {
			dev.log.Debug("Port %d is taken by %s:%d: %s, retry with the next port",
				extPort, intaddr, intPort, desc)
		} else if err := dev.r.MapPort(protocol, intPort, extPort, desc, mapTimeout); err != nil {
			dev.log.Debug("Map port failed. Protocol %s Internal %d External %d. %s",
				protocol, intPort, extPort, err)
		} else {
			dev.log.Info("Mapped Protocol %s Internal %d External %d.", protocol,
				intPort, extPort)

			dev.wg.Add(1)

			mappedPort <- extPort

			defer func(extPort uint16) {
				updateTimer.Stop()

				dev.log.Debug("Unmap protocol %s external port %d", protocol, extPort)
				dev.r.UnmapPort(protocol, intPort, extPort)

				dev.wg.Done()
			}(extPort)

			for {
				select {
				case <-updateTimer.C:
					if err := dev.r.MapPort(protocol, intPort, extPort, desc, mapTimeout); err != nil {
						dev.log.Error("Renewing port mapping from external port %d to internal port %d failed with %s",
							intPort, extPort, err)
					} else {
						dev.log.Debug("Renewed port mapping from external port %d to internal port %d.",
							intPort, extPort)
					}

					updateTimer.Reset(mapUpdateTimeout)
				case _, _ = <-dev.closer:
					return
				}
			}
		}
	}

	dev.log.Debug("Unable to map port %d~%d", intPort, intPort+maxRetries)
	mappedPort <- 0
}

// UnmapAllPorts stops mapping all ports from this mapper and attempts to unmap
// them.
func (dev *Mapper) UnmapAllPorts() {
	close(dev.closer)
	dev.wg.Wait()
	dev.log.Info("Unmapped all ports")
}
<commit_msg>fix logging error<commit_after>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package nat

import (
	"errors"
	"net"
	"sync"
	"time"

	"github.com/ava-labs/gecko/utils/logging"
)

const (
	mapTimeout       = 30 * time.Minute
	mapUpdateTimeout = mapTimeout / 2
	maxRetries       = 20
)

// Router describes the functionality that a network device must support to be
// able to open ports to an external IP.
type Router interface {
	MapPort(protocol string, intPort, extPort uint16, desc string, duration time.Duration) error
	UnmapPort(protocol string, intPort, extPort uint16) error
	ExternalIP() (net.IP, error)
	GetPortMappingEntry(extPort uint16, protocol string) (
		InternalIP string,
		InternalPort uint16,
		Description string,
		err error,
	)
}

// GetRouter returns a router on the current network.
func GetRouter() Router {
	if r := getUPnPRouter(); r != nil {
		return r
	}
	if r := getPMPRouter(); r != nil {
		return r
	}

	return NewNoRouter()
}

// Mapper attempts to open a set of ports on a router
type Mapper struct {
	log    logging.Logger
	r      Router
	closer chan struct{}
	wg     sync.WaitGroup
}

// NewPortMapper returns an initialized mapper
func NewPortMapper(log logging.Logger, r Router) Mapper {
	return Mapper{
		log:    log,
		r:      r,
		closer: make(chan struct{}),
	}
}

// Map sets up port mapping using given protocol, internal and external ports
// and returns the final port mapped. It returns 0 if mapping failed after the
// maximun number of retries
func (dev *Mapper) Map(protocol string, intPort uint16, desc string) (uint16, error) {
	mappedPort := make(chan uint16)

	go dev.keepPortMapping(mappedPort, protocol, intPort, desc)

	port := <-mappedPort
	if port == 0 {
		return 0, errors.New("failed to map port")
	}
	return port, nil
}

// keepPortMapping runs in the background to keep a port mapped. It renews the
// the port mapping in mapUpdateTimeout.
func (dev *Mapper) keepPortMapping(mappedPort chan<- uint16, protocol string,
	intPort uint16, desc string) {
	updateTimer := time.NewTimer(mapUpdateTimeout)

	for i := 0; i <= maxRetries; i++ {
		extPort := intPort + uint16(i)
		if addr, port, desc, err := dev.r.GetPortMappingEntry(extPort, protocol); err == nil {
			dev.log.Debug("Port %d is taken by %s:%d: %s, retry with the next port",
				extPort, addr, port, desc)
		} else if err := dev.r.MapPort(protocol, intPort, extPort, desc, mapTimeout); err != nil {
			dev.log.Debug("Map port failed. Protocol %s Internal %d External %d. %s",
				protocol, intPort, extPort, err)
		} else {
			dev.log.Info("Mapped Protocol %s Internal %d External %d.", protocol,
				intPort, extPort)

			dev.wg.Add(1)

			mappedPort <- extPort

			defer func(extPort uint16) {
				updateTimer.Stop()

				dev.log.Debug("Unmap protocol %s external port %d", protocol, extPort)
				dev.r.UnmapPort(protocol, intPort, extPort)

				dev.wg.Done()
			}(extPort)

			for {
				select {
				case <-updateTimer.C:
					if err := dev.r.MapPort(protocol, intPort, extPort, desc, mapTimeout); err != nil {
						dev.log.Error("Renewing port mapping from external port %d to internal port %d failed with %s",
							intPort, extPort, err)
					} else {
						dev.log.Debug("Renewed port mapping from external port %d to internal port %d.",
							intPort, extPort)
					}

					updateTimer.Reset(mapUpdateTimeout)
				case _, _ = <-dev.closer:
					return
				}
			}
		}
	}

	dev.log.Debug("Unable to map port %d~%d", intPort, intPort+maxRetries)
	mappedPort <- 0
}

// UnmapAllPorts stops mapping all ports from this mapper and attempts to unmap
// them.
func (dev *Mapper) UnmapAllPorts() {
	close(dev.closer)
	dev.wg.Wait()
	dev.log.Info("Unmapped all ports")
}
<|endoftext|>


<commit_before>package psh

import (
	"fmt"
	"os/exec"
	"polydawn.net/gosh/iox"
)

func Sh(cmd string) sh {
	var cmdt commandTemplate
	cmdt.cmd = cmd
	cmdt.env = getOsEnv()
	cmdt.OkExit = []int{0}
	return enclose(&cmdt)
}

type sh func(args ...interface{}) sh

// private type, used exactly once to create a const nobody else can create so we can use it as a flag to trigger private behavior
type expose_t bool

const expose expose_t = true

type exposer struct{ cmdt *commandTemplate }

func closure(cmdt commandTemplate, args ...interface{}) sh {
	if len(args) == 0 {
		// an empty call is a synonym for sh.Run().
		// if you want to just get a RunningCommand reference to track, use sh.Start() instead.
		enclose(&cmdt).Run()
		return nil
	} else if args[0] == expose {
		// produce a function that when called with an exposer, exposes its cmdt.
		return func(x ...interface{}) sh {
			t := x[0].(*exposer)
			t.cmdt = &cmdt
			return nil
		}
	} else {
		// examine each of the arguments, modify our (already forked) cmdt, and
		//  return a new callable sh closure with the newly baked command template.
		for _, rarg := range args {
			switch arg := rarg.(type) {
			case string:
				cmdt.bakeArgs(arg)
			case Env:
				cmdt.bakeEnv(arg)
			case ClearEnv:
				cmdt.clearEnv()
			case Opts:
				cmdt.bakeOpts(arg)
			default:
				panic(IncomprehensibleCommandModifier{wat: &rarg})
			}
		}
		return enclose(&cmdt)
	}
}

func (f sh) expose() *commandTemplate {
	var t exposer
	f(expose)(&t)
	return t.cmdt
}

func enclose(cmdt *commandTemplate) sh {
	return func(x ...interface{}) sh {
		return closure(*cmdt, x...)
	}
}

func (f sh) BakeArgs(args ...string) sh {
	return enclose(f.expose().bakeArgs(args...))
}

func (cmdt *commandTemplate) bakeArgs(args ...string) *commandTemplate {
	cmdt.args = append(cmdt.args, args...)
	return cmdt
}

func (f sh) BakeEnv(args Env) sh {
	return enclose(f.expose().bakeEnv(args))
}

func (cmdt *commandTemplate) bakeEnv(args Env) *commandTemplate {
	for k, v := range args {
		if v == "" {
			delete(cmdt.env, k)
		} else {
			cmdt.env[k] = v
		}
	}
	return cmdt
}

func (f sh) ClearEnv() sh {
	return enclose(f.expose().clearEnv())
}

func (cmdt *commandTemplate) clearEnv() *commandTemplate {
	cmdt.env = make(map[string]string)
	return cmdt
}

func (f sh) BakeOpts(args ...Opts) sh {
	return enclose(f.expose().bakeOpts(args...))
}

func (cmdt *commandTemplate) bakeOpts(args ...Opts) *commandTemplate {
	for _, arg := range args {
		if arg.Cwd != nil {
			cmdt.Cwd = arg.Cwd
		}
		if arg.In != nil {
			cmdt.In = arg.In
		}
		if arg.Out != nil {
			cmdt.Out = arg.Out
		}
		if arg.Err != nil {
			cmdt.Err = arg.Err
		}
		if arg.OkExit != nil {
			cmdt.OkExit = arg.OkExit
		}
	}
	return cmdt
}

/**
 * Starts execution of the command.  Returns a reference to a RunningCommand,
 * which can be used to track execution of the command, configure exit listeners,
 * etc.
 */
func (f sh) Start() *RunningCommand {
	cmdt := f.expose()
	rcmd := exec.Command(cmdt.cmd, cmdt.args...)

	// set up env
	if cmdt.env != nil {
		rcmd.Env = make([]string, len(cmdt.env))
		i := 0
		for k, v := range cmdt.env {
			rcmd.Env[i] = fmt.Sprintf("%s=%s", k, v)
			i++
		}
	}

	// set up opts (cwd/stdin/stdout/stderr)
	if cmdt.Cwd != nil {
		rcmd.Dir = *cmdt.Cwd
	}
	if cmdt.In != nil {
		switch in := cmdt.In.(type) {
		case sh:
			//TODO something marvelous
			panic(fmt.Errorf("not yet implemented"))
		default:
			inreader := iox.ReaderFromInterface(in)
			//TODO if this is nil, raise a (typed) panic (which we don't have a type for yet)
			rcmd.Stdin = inreader
		}
	}
	if cmdt.Out != nil {
		out := iox.WriterFromInterface(cmdt.Out)
		//TODO if this is nil, raise a (typed) panic (which we don't have a type for yet)
		rcmd.Stdout = out
	}
	if cmdt.Err != nil {
		if cmdt.Err == cmdt.Out {
			rcmd.Stderr = rcmd.Stdout
		} else {
			out := iox.WriterFromInterface(cmdt.Err)
			//TODO if this is nil, raise a (typed) panic (which we don't have a type for yet)
			rcmd.Stderr = out
		}
	}

	// go time
	cmd := NewRunningCommand(rcmd)
	cmd.Start()
	return cmd
}

func (f sh) Run() {
	cmdt := f.expose()
	cmd := f.Start()
	cmd.Wait()
	exitCode := cmd.GetExitCode()
	for _, okcode := range cmdt.OkExit {
		if exitCode == okcode {
			return
		}
	}
	panic(FailureExitCode{cmdname: cmdt.cmd, code: exitCode})
}
<commit_msg>rename the closure function type the Sh() conveniences are based on to be public so that it's possible for other libraries to create functions of their own that return that type.<commit_after>package psh

import (
	"fmt"
	"os/exec"
	"polydawn.net/gosh/iox"
)

func Sh(cmd string) Shfn {
	var cmdt commandTemplate
	cmdt.cmd = cmd
	cmdt.env = getOsEnv()
	cmdt.OkExit = []int{0}
	return enclose(&cmdt)
}

type Shfn func(args ...interface{}) Shfn

// private type, used exactly once to create a const nobody else can create so we can use it as a flag to trigger private behavior
type expose_t bool

const expose expose_t = true

type exposer struct{ cmdt *commandTemplate }

func closure(cmdt commandTemplate, args ...interface{}) Shfn {
	if len(args) == 0 {
		// an empty call is a synonym for Shfn.Run().
		// if you want to just get a RunningCommand reference to track, use Shfn.Start() instead.
		enclose(&cmdt).Run()
		return nil
	} else if args[0] == expose {
		// produce a function that when called with an exposer, exposes its cmdt.
		return func(x ...interface{}) Shfn {
			t := x[0].(*exposer)
			t.cmdt = &cmdt
			return nil
		}
	} else {
		// examine each of the arguments, modify our (already forked) cmdt, and
		//  return a new callable Shfn closure with the newly baked command template.
		for _, rarg := range args {
			switch arg := rarg.(type) {
			case string:
				cmdt.bakeArgs(arg)
			case Env:
				cmdt.bakeEnv(arg)
			case ClearEnv:
				cmdt.clearEnv()
			case Opts:
				cmdt.bakeOpts(arg)
			default:
				panic(IncomprehensibleCommandModifier{wat: &rarg})
			}
		}
		return enclose(&cmdt)
	}
}

func (f Shfn) expose() *commandTemplate {
	var t exposer
	f(expose)(&t)
	return t.cmdt
}

func enclose(cmdt *commandTemplate) Shfn {
	return func(x ...interface{}) Shfn {
		return closure(*cmdt, x...)
	}
}

func (f Shfn) BakeArgs(args ...string) Shfn {
	return enclose(f.expose().bakeArgs(args...))
}

func (cmdt *commandTemplate) bakeArgs(args ...string) *commandTemplate {
	cmdt.args = append(cmdt.args, args...)
	return cmdt
}

func (f Shfn) BakeEnv(args Env) Shfn {
	return enclose(f.expose().bakeEnv(args))
}

func (cmdt *commandTemplate) bakeEnv(args Env) *commandTemplate {
	for k, v := range args {
		if v == "" {
			delete(cmdt.env, k)
		} else {
			cmdt.env[k] = v
		}
	}
	return cmdt
}

func (f Shfn) ClearEnv() Shfn {
	return enclose(f.expose().clearEnv())
}

func (cmdt *commandTemplate) clearEnv() *commandTemplate {
	cmdt.env = make(map[string]string)
	return cmdt
}

func (f Shfn) BakeOpts(args ...Opts) Shfn {
	return enclose(f.expose().bakeOpts(args...))
}

func (cmdt *commandTemplate) bakeOpts(args ...Opts) *commandTemplate {
	for _, arg := range args {
		if arg.Cwd != nil {
			cmdt.Cwd = arg.Cwd
		}
		if arg.In != nil {
			cmdt.In = arg.In
		}
		if arg.Out != nil {
			cmdt.Out = arg.Out
		}
		if arg.Err != nil {
			cmdt.Err = arg.Err
		}
		if arg.OkExit != nil {
			cmdt.OkExit = arg.OkExit
		}
	}
	return cmdt
}

/**
 * Starts execution of the command.  Returns a reference to a RunningCommand,
 * which can be used to track execution of the command, configure exit listeners,
 * etc.
 */
func (f Shfn) Start() *RunningCommand {
	cmdt := f.expose()
	rcmd := exec.Command(cmdt.cmd, cmdt.args...)

	// set up env
	if cmdt.env != nil {
		rcmd.Env = make([]string, len(cmdt.env))
		i := 0
		for k, v := range cmdt.env {
			rcmd.Env[i] = fmt.Sprintf("%s=%s", k, v)
			i++
		}
	}

	// set up opts (cwd/stdin/stdout/stderr)
	if cmdt.Cwd != nil {
		rcmd.Dir = *cmdt.Cwd
	}
	if cmdt.In != nil {
		switch in := cmdt.In.(type) {
		case Shfn:
			//TODO something marvelous
			panic(fmt.Errorf("not yet implemented"))
		default:
			inreader := iox.ReaderFromInterface(in)
			//TODO if this is nil, raise a (typed) panic (which we don't have a type for yet)
			rcmd.Stdin = inreader
		}
	}
	if cmdt.Out != nil {
		out := iox.WriterFromInterface(cmdt.Out)
		//TODO if this is nil, raise a (typed) panic (which we don't have a type for yet)
		rcmd.Stdout = out
	}
	if cmdt.Err != nil {
		if cmdt.Err == cmdt.Out {
			rcmd.Stderr = rcmd.Stdout
		} else {
			out := iox.WriterFromInterface(cmdt.Err)
			//TODO if this is nil, raise a (typed) panic (which we don't have a type for yet)
			rcmd.Stderr = out
		}
	}

	// go time
	cmd := NewRunningCommand(rcmd)
	cmd.Start()
	return cmd
}

func (f Shfn) Run() {
	cmdt := f.expose()
	cmd := f.Start()
	cmd.Wait()
	exitCode := cmd.GetExitCode()
	for _, okcode := range cmdt.OkExit {
		if exitCode == okcode {
			return
		}
	}
	panic(FailureExitCode{cmdname: cmdt.cmd, code: exitCode})
}
<|endoftext|>


<commit_before>// Package pages provides a data structure for a web pages.
package pages

import (
	"bytes"
	"encoding/base64"
	"errors"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"path"

	"github.com/ChristianSiegert/go-packages/chttp"
	"github.com/ChristianSiegert/go-packages/forms"
	"github.com/ChristianSiegert/go-packages/html"
	"github.com/ChristianSiegert/go-packages/i18n/languages"
	"github.com/ChristianSiegert/go-packages/sessions"
	"golang.org/x/net/context"
)

// Whether NewPage and MustNewPage should reload the provided template.
// Reloading templates on each request is useful to see changes without
// recompiling. In production, reloading should be disabled.
var ReloadTemplates = false

// Path to template that is used as root template when
// Page.Serve[Empty|NotFound|Unauthorized|WithError] is called.
var RootTemplatePath = "./templates/index.html"

// Templates that are used as content template when Page.ServeEmpty or
// Page.ServeNotFound is called.
var (
	TemplateEmpty    = MustNewTemplate("", nil)
	TemplateNotFound = MustNewTemplate("./templates/404-not-found.html", nil)
)

// Template that is used as content template when Page.Error is called.
var TemplateError = MustNewTemplateWithRoot("./templates/error.html", "./templates/500-internal-server-error.html", nil)

// SignInUrl is the URL to the page that users are redirected to when
// Page.RequireSignIn is called. If a %s placeholder is present in
// SignInUrl.Path, it is replaced by the page’s language code. E.g.
// “/%s/sign-in” becomes “/en/sign-in” if the page’s language code is “en”.
var SignInUrl = &url.URL{
	Path: "/%s/sign-in",
}

// Page represents a web page.
type Page struct {
	Breadcrumbs []*Breadcrumb

	Data map[string]interface{}

	// Form is an instance of *forms.Form bound to the request.
	Form *forms.Form

	Language *languages.Language

	// Name of the page. Useful in the root template, e.g. to style the
	// navigation link of the current page.
	Name string

	request *http.Request

	responseWriter http.ResponseWriter

	Session *sessions.Session

	Template *Template

	// Title of the page that templates can use to populate the HTML <title>
	// element.
	Title string
}

func NewPage(ctx context.Context, tpl *Template) (*Page, error) {
	responseWriter, request, ok := chttp.FromContext(ctx)
	if !ok {
		return nil, fmt.Errorf("pages.NewPage: http.ResponseWriter and http.Request are not provided by ctx.")
	}

	language, ok := languages.FromContext(ctx)
	if !ok {
		return nil, fmt.Errorf("pages.NewPage: languages.Language is not provided by ctx.")
	}

	session, ok := sessions.FromContext(ctx)
	if !ok {
		return nil, fmt.Errorf("pages.NewPage: sessions.Session is not provided by ctx.")
	}

	if ReloadTemplates {
		if err := tpl.Reload(); err != nil {
			return nil, err
		}
	}

	form, err := forms.New(ctx)
	if err != nil {
		return nil, err
	}

	page := &Page{
		Form:           form,
		Language:       language,
		request:        request,
		responseWriter: responseWriter,
		Session:        session,
		Template:       tpl,
	}

	return page, nil
}

// MustNewPage calls NewPage and panics on error.
func MustNewPage(ctx context.Context, tpl *Template) *Page {
	page, err := NewPage(ctx, tpl)
	if err != nil {
		panic("pages.MustNewPage: " + err.Error())
	}
	return page
}

func (p *Page) AddBreadcrumb(title string, url *url.URL) *Breadcrumb {
	breadcrumb := &Breadcrumb{
		Title: title,
		Url:   url,
	}

	p.Breadcrumbs = append(p.Breadcrumbs, breadcrumb)
	return breadcrumb
}

func (p *Page) Redirect(urlStr string, code int) {
	http.Redirect(p.responseWriter, p.request, urlStr, code)
}

// RequireSignIn redirects users to the sign-in page specified by SignInUrl.
// If SignInUrl.RawQuery is empty, the query parameters “r” (referrer) and “t”
// (title of the referrer page) are appended. This allows the sign-in page to
// display a message that page <title> is access restricted, and after
// successful authentication, users can be redirected to <referrer>, the page
// they came from.
func (p *Page) RequireSignIn(pageTitle string) {
	u := &url.URL{
		Scheme:   SignInUrl.Scheme,
		Opaque:   SignInUrl.Opaque,
		User:     SignInUrl.User,
		Host:     SignInUrl.Host,
		Path:     fmt.Sprintf(SignInUrl.Path, p.Language.Code()),
		Fragment: SignInUrl.Fragment,
	}

	if SignInUrl.RawQuery == "" {
		query := &url.Values{}
		query.Add("r", p.request.URL.Path)
		query.Add("t", base64.URLEncoding.EncodeToString([]byte(pageTitle))) // TODO: Sign or encrypt parameter to prevent tempering by users
		u.RawQuery = query.Encode()
	}

	p.Redirect(u.String(), http.StatusSeeOther)
}

// Error serves an error page with a generic error message. Err is not displayed
// to the user but written to the error log.
func (p *Page) Error(err error) {
	log.Println(err.Error())

	if TemplateError == nil {
		log.Println("pages.Page.Error: TemplateError is nil.")
		http.Error(p.responseWriter, "Internal Server Error", http.StatusInternalServerError)
		return
	}

	buffer := bytes.NewBuffer([]byte{})

	p.Data = map[string]interface{}{
		"Error":          err,
		"IsDevAppServer": true,
	}

	if err := TemplateError.template.ExecuteTemplate(buffer, path.Base(TemplateError.rootTemplatePath), p); err != nil {
		log.Printf("pages.Page.Error: Executing template failed: %s\n", err)
		http.Error(p.responseWriter, "Internal Server Error", http.StatusInternalServerError)
		return
	}

	b := html.RemoveWhitespace(buffer.Bytes())

	if _, err := bytes.NewBuffer(b).WriteTo(p.responseWriter); err != nil {
		log.Printf("pages.Page.Error: Writing template to buffer failed: %s\n", err)
		http.Error(p.responseWriter, "Internal Server Error", http.StatusInternalServerError)
	}
}

// Serve serves the root template specified by RootTemplatePath with the content
// template specified by p.Template. HTML comments and whitespace are stripped.
// If p.Template is nil, an empty content template is embedded.
func (p *Page) Serve() {
	buffer := bytes.NewBuffer([]byte{})

	if p.Template == nil {
		p.Template = TemplateEmpty
	}

	// If still nil
	if p.Template == nil {
		p.Error(errors.New("pages.Page.Serve: p.Template is nil."))
		return
	}

	if err := p.Template.template.ExecuteTemplate(buffer, path.Base(p.Template.rootTemplatePath), p); err != nil {
		p.Error(err)
		return
	}

	b := html.RemoveWhitespace(buffer.Bytes())

	if _, err := bytes.NewBuffer(b).WriteTo(p.responseWriter); err != nil {
		p.Error(err)
	}
}

// ServeEmpty serves the root template without content template.
func (p *Page) ServeEmpty() {
	p.Template = TemplateEmpty
	p.Serve()
}

// ServeNotFound serves a page that tells the user the requested page does not
// exist.
func (p *Page) ServeNotFound() {
	p.responseWriter.WriteHeader(http.StatusNotFound)
	p.Template = TemplateNotFound
	p.Serve()
}

// ServeUnauthorized serves a page that tells the user the requested page cannot
// be accessed due to insufficient access rights.
func (p *Page) ServeUnauthorized() {
	p.Session.AddFlashError(p.T("err_unauthorized_access"))
	p.responseWriter.WriteHeader(http.StatusUnauthorized)
	p.ServeEmpty()
}

// ServeWithError is similar to Serve, but additionally an error flash message
// is displayed to the user saying that an internal problem occurred. Err is not
// displayed but written to the error log. This method is useful if the user
// should be informed of a problem while the state, e.g. a filled in form, is
// preserved.
func (p *Page) ServeWithError(err error) {
	// context := appengine.NewContext(p.Request)
	// context.Errorf(err.Error())
	p.Session.AddFlashError(p.T("err_internal_server_error"))
	p.Serve()
}

// T returns the translation associated with translationId. If p.Language
// is nil, translationId is returned.
func (p *Page) T(translationId string, templateData ...map[string]interface{}) string {
	if p.Language == nil {
		return translationId
	}
	return p.Language.T(translationId, templateData...)
}

func Error(ctx context.Context, err error) {
	page := MustNewPage(ctx, TemplateError)
	page.Error(err)
}
<commit_msg>Replaced “fmt.Errorf” with “errors.New” in package “pages” where applicable.<commit_after>// Package pages provides a data structure for a web pages.
package pages

import (
	"bytes"
	"encoding/base64"
	"errors"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"path"

	"github.com/ChristianSiegert/go-packages/chttp"
	"github.com/ChristianSiegert/go-packages/forms"
	"github.com/ChristianSiegert/go-packages/html"
	"github.com/ChristianSiegert/go-packages/i18n/languages"
	"github.com/ChristianSiegert/go-packages/sessions"
	"golang.org/x/net/context"
)

// Whether NewPage and MustNewPage should reload the provided template.
// Reloading templates on each request is useful to see changes without
// recompiling. In production, reloading should be disabled.
var ReloadTemplates = false

// Path to template that is used as root template when
// Page.Serve[Empty|NotFound|Unauthorized|WithError] is called.
var RootTemplatePath = "./templates/index.html"

// Templates that are used as content template when Page.ServeEmpty or
// Page.ServeNotFound is called.
var (
	TemplateEmpty    = MustNewTemplate("", nil)
	TemplateNotFound = MustNewTemplate("./templates/404-not-found.html", nil)
)

// Template that is used as content template when Page.Error is called.
var TemplateError = MustNewTemplateWithRoot("./templates/error.html", "./templates/500-internal-server-error.html", nil)

// SignInUrl is the URL to the page that users are redirected to when
// Page.RequireSignIn is called. If a %s placeholder is present in
// SignInUrl.Path, it is replaced by the page’s language code. E.g.
// “/%s/sign-in” becomes “/en/sign-in” if the page’s language code is “en”.
var SignInUrl = &url.URL{
	Path: "/%s/sign-in",
}

// Page represents a web page.
type Page struct {
	Breadcrumbs []*Breadcrumb

	Data map[string]interface{}

	// Form is an instance of *forms.Form bound to the request.
	Form *forms.Form

	Language *languages.Language

	// Name of the page. Useful in the root template, e.g. to style the
	// navigation link of the current page.
	Name string

	request *http.Request

	responseWriter http.ResponseWriter

	Session *sessions.Session

	Template *Template

	// Title of the page that templates can use to populate the HTML <title>
	// element.
	Title string
}

func NewPage(ctx context.Context, tpl *Template) (*Page, error) {
	responseWriter, request, ok := chttp.FromContext(ctx)
	if !ok {
		return nil, errors.New("pages.NewPage: http.ResponseWriter and http.Request are not provided by ctx.")
	}

	language, ok := languages.FromContext(ctx)
	if !ok {
		return nil, errors.New("pages.NewPage: languages.Language is not provided by ctx.")
	}

	session, ok := sessions.FromContext(ctx)
	if !ok {
		return nil, errors.New("pages.NewPage: sessions.Session is not provided by ctx.")
	}

	if ReloadTemplates {
		if err := tpl.Reload(); err != nil {
			return nil, err
		}
	}

	form, err := forms.New(ctx)
	if err != nil {
		return nil, err
	}

	page := &Page{
		Form:           form,
		Language:       language,
		request:        request,
		responseWriter: responseWriter,
		Session:        session,
		Template:       tpl,
	}

	return page, nil
}

// MustNewPage calls NewPage and panics on error.
func MustNewPage(ctx context.Context, tpl *Template) *Page {
	page, err := NewPage(ctx, tpl)
	if err != nil {
		panic("pages.MustNewPage: " + err.Error())
	}
	return page
}

func (p *Page) AddBreadcrumb(title string, url *url.URL) *Breadcrumb {
	breadcrumb := &Breadcrumb{
		Title: title,
		Url:   url,
	}

	p.Breadcrumbs = append(p.Breadcrumbs, breadcrumb)
	return breadcrumb
}

func (p *Page) Redirect(urlStr string, code int) {
	http.Redirect(p.responseWriter, p.request, urlStr, code)
}

// RequireSignIn redirects users to the sign-in page specified by SignInUrl.
// If SignInUrl.RawQuery is empty, the query parameters “r” (referrer) and “t”
// (title of the referrer page) are appended. This allows the sign-in page to
// display a message that page <title> is access restricted, and after
// successful authentication, users can be redirected to <referrer>, the page
// they came from.
func (p *Page) RequireSignIn(pageTitle string) {
	u := &url.URL{
		Scheme:   SignInUrl.Scheme,
		Opaque:   SignInUrl.Opaque,
		User:     SignInUrl.User,
		Host:     SignInUrl.Host,
		Path:     fmt.Sprintf(SignInUrl.Path, p.Language.Code()),
		Fragment: SignInUrl.Fragment,
	}

	if SignInUrl.RawQuery == "" {
		query := &url.Values{}
		query.Add("r", p.request.URL.Path)
		query.Add("t", base64.URLEncoding.EncodeToString([]byte(pageTitle))) // TODO: Sign or encrypt parameter to prevent tempering by users
		u.RawQuery = query.Encode()
	}

	p.Redirect(u.String(), http.StatusSeeOther)
}

// Error serves an error page with a generic error message. Err is not displayed
// to the user but written to the error log.
func (p *Page) Error(err error) {
	log.Println(err.Error())

	if TemplateError == nil {
		log.Println("pages.Page.Error: TemplateError is nil.")
		http.Error(p.responseWriter, "Internal Server Error", http.StatusInternalServerError)
		return
	}

	buffer := bytes.NewBuffer([]byte{})

	p.Data = map[string]interface{}{
		"Error":          err,
		"IsDevAppServer": true,
	}

	if err := TemplateError.template.ExecuteTemplate(buffer, path.Base(TemplateError.rootTemplatePath), p); err != nil {
		log.Printf("pages.Page.Error: Executing template failed: %s\n", err)
		http.Error(p.responseWriter, "Internal Server Error", http.StatusInternalServerError)
		return
	}

	b := html.RemoveWhitespace(buffer.Bytes())

	if _, err := bytes.NewBuffer(b).WriteTo(p.responseWriter); err != nil {
		log.Printf("pages.Page.Error: Writing template to buffer failed: %s\n", err)
		http.Error(p.responseWriter, "Internal Server Error", http.StatusInternalServerError)
	}
}

// Serve serves the root template specified by RootTemplatePath with the content
// template specified by p.Template. HTML comments and whitespace are stripped.
// If p.Template is nil, an empty content template is embedded.
func (p *Page) Serve() {
	buffer := bytes.NewBuffer([]byte{})

	if p.Template == nil {
		p.Template = TemplateEmpty
	}

	// If still nil
	if p.Template == nil {
		p.Error(errors.New("pages.Page.Serve: p.Template is nil."))
		return
	}

	if err := p.Template.template.ExecuteTemplate(buffer, path.Base(p.Template.rootTemplatePath), p); err != nil {
		p.Error(err)
		return
	}

	b := html.RemoveWhitespace(buffer.Bytes())

	if _, err := bytes.NewBuffer(b).WriteTo(p.responseWriter); err != nil {
		p.Error(err)
	}
}

// ServeEmpty serves the root template without content template.
func (p *Page) ServeEmpty() {
	p.Template = TemplateEmpty
	p.Serve()
}

// ServeNotFound serves a page that tells the user the requested page does not
// exist.
func (p *Page) ServeNotFound() {
	p.responseWriter.WriteHeader(http.StatusNotFound)
	p.Template = TemplateNotFound
	p.Serve()
}

// ServeUnauthorized serves a page that tells the user the requested page cannot
// be accessed due to insufficient access rights.
func (p *Page) ServeUnauthorized() {
	p.Session.AddFlashError(p.T("err_unauthorized_access"))
	p.responseWriter.WriteHeader(http.StatusUnauthorized)
	p.ServeEmpty()
}

// ServeWithError is similar to Serve, but additionally an error flash message
// is displayed to the user saying that an internal problem occurred. Err is not
// displayed but written to the error log. This method is useful if the user
// should be informed of a problem while the state, e.g. a filled in form, is
// preserved.
func (p *Page) ServeWithError(err error) {
	// context := appengine.NewContext(p.Request)
	// context.Errorf(err.Error())
	p.Session.AddFlashError(p.T("err_internal_server_error"))
	p.Serve()
}

// T returns the translation associated with translationId. If p.Language
// is nil, translationId is returned.
func (p *Page) T(translationId string, templateData ...map[string]interface{}) string {
	if p.Language == nil {
		return translationId
	}
	return p.Language.T(translationId, templateData...)
}

func Error(ctx context.Context, err error) {
	page := MustNewPage(ctx, TemplateError)
	page.Error(err)
}
<|endoftext|>


<commit_before>package armhelpers

import (
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/Azure/azure-sdk-for-go/arm/compute"
	"github.com/Azure/azure-sdk-for-go/arm/network"
	"github.com/Azure/azure-sdk-for-go/arm/resources/resources"
	"github.com/Azure/azure-sdk-for-go/arm/resources/subscriptions"
	"github.com/Azure/azure-sdk-for-go/arm/storage"
	"github.com/Azure/go-autorest/autorest"
	"github.com/Azure/go-autorest/autorest/adal"
	"github.com/Azure/go-autorest/autorest/azure"
	"github.com/Azure/go-autorest/autorest/to"
	log "github.com/Sirupsen/logrus"
	"github.com/mitchellh/go-homedir"

	"github.com/Azure/acs-engine/pkg/acsengine"
)

const (
	// AcsEngineClientID is the AAD ClientID for the CLI native application
	AcsEngineClientID = "76e0feec-6b7f-41f0-81a7-b1b944520261"

	// ApplicationDir is the name of the dir where the token is cached
	ApplicationDir = ".acsengine"
)

var (
	// RequiredResourceProviders is the list of Azure Resource Providers needed for ACS-Engine to function
	RequiredResourceProviders = []string{"Microsoft.Compute", "Microsoft.Storage", "Microsoft.Network"}
)

// AzureClient implements the `ACSEngineClient` interface.
// This client is backed by real Azure clients talking to an ARM endpoint.
type AzureClient struct {
	environment azure.Environment

	deploymentsClient     resources.DeploymentsClient
	resourcesClient       resources.GroupClient
	storageAccountsClient storage.AccountsClient
	interfacesClient      network.InterfacesClient
	groupsClient          resources.GroupsClient
	providersClient       resources.ProvidersClient
	subscriptionsClient   subscriptions.GroupClient
	virtualMachinesClient compute.VirtualMachinesClient
}

// NewAzureClientWithDeviceAuth returns an AzureClient by having a user complete a device authentication flow
func NewAzureClientWithDeviceAuth(env azure.Environment, subscriptionID string) (*AzureClient, error) {
	oauthConfig, tenantID, err := getOAuthConfig(env, subscriptionID)
	if err != nil {
		return nil, err
	}

	home, err := homedir.Dir()
	if err != nil {
		return nil, fmt.Errorf("Failed to get user home directory to look for cached token: %q", err)
	}
	cachePath := filepath.Join(home, ApplicationDir, "cache", fmt.Sprintf("%s_%s.token.json", tenantID, AcsEngineClientID))

	rawToken, err := tryLoadCachedToken(cachePath)
	if err != nil {
		return nil, err
	}

	var armSpt *adal.ServicePrincipalToken
	if rawToken != nil {
		armSpt, err = adal.NewServicePrincipalTokenFromManualToken(*oauthConfig, AcsEngineClientID, env.ServiceManagementEndpoint, *rawToken, tokenCallback(cachePath))
		if err != nil {
			return nil, err
		}
		err = armSpt.Refresh()
		if err != nil {
			log.Warnf("Refresh token failed. Will fallback to device auth. %q", err)
		} else {
			adSpt, err := adal.NewServicePrincipalTokenFromManualToken(*oauthConfig, AcsEngineClientID, env.GraphEndpoint, armSpt.Token)
			if err != nil {
				return nil, err
			}
			return getClient(env, subscriptionID, armSpt, adSpt)
		}
	}

	client := &autorest.Client{}

	deviceCode, err := adal.InitiateDeviceAuth(client, *oauthConfig, AcsEngineClientID, env.ServiceManagementEndpoint)
	if err != nil {
		return nil, err
	}
	log.Warnln(*deviceCode.Message)
	deviceToken, err := adal.WaitForUserCompletion(client, deviceCode)
	if err != nil {
		return nil, err
	}

	armSpt, err = adal.NewServicePrincipalTokenFromManualToken(*oauthConfig, AcsEngineClientID, env.ServiceManagementEndpoint, *deviceToken, tokenCallback(cachePath))
	if err != nil {
		return nil, err
	}
	armSpt.Refresh()

	adRawToken := armSpt.Token
	adRawToken.Resource = env.GraphEndpoint
	adSpt, err := adal.NewServicePrincipalTokenFromManualToken(*oauthConfig, AcsEngineClientID, env.GraphEndpoint, adRawToken)
	if err != nil {
		return nil, err
	}

	return getClient(env, subscriptionID, armSpt, adSpt)
}

// NewAzureClientWithClientSecret returns an AzureClient via client_id and client_secret
func NewAzureClientWithClientSecret(env azure.Environment, subscriptionID, clientID, clientSecret string) (*AzureClient, error) {
	oauthConfig, _, err := getOAuthConfig(env, subscriptionID)
	if err != nil {
		return nil, err
	}

	armSpt, err := adal.NewServicePrincipalToken(*oauthConfig, clientID, clientSecret, env.ServiceManagementEndpoint)
	if err != nil {
		return nil, err
	}
	adSpt, err := adal.NewServicePrincipalToken(*oauthConfig, clientID, clientSecret, env.GraphEndpoint)
	if err != nil {
		return nil, err
	}

	return getClient(env, subscriptionID, armSpt, adSpt)
}

// NewAzureClientWithClientCertificate returns an AzureClient via client_id and jwt certificate assertion
func NewAzureClientWithClientCertificate(env azure.Environment, subscriptionID, clientID, certificatePath, privateKeyPath string) (*AzureClient, error) {
	oauthConfig, _, err := getOAuthConfig(env, subscriptionID)
	if err != nil {
		return nil, err
	}

	certificateData, err := ioutil.ReadFile(certificatePath)
	if err != nil {
		return nil, fmt.Errorf("Failed to read certificate: %q", err)
	}

	block, _ := pem.Decode(certificateData)
	if block == nil {
		return nil, fmt.Errorf("Failed to decode pem block from certificate")
	}

	certificate, err := x509.ParseCertificate(block.Bytes)
	if err != nil {
		return nil, fmt.Errorf("Failed to parse certificate: %q", err)
	}

	privateKey, err := parseRsaPrivateKey(privateKeyPath)
	if err != nil {
		return nil, fmt.Errorf("Failed to parse rsa private key: %q", err)
	}

	armSpt, err := adal.NewServicePrincipalTokenFromCertificate(*oauthConfig, clientID, certificate, privateKey, env.ServiceManagementEndpoint)
	if err != nil {
		return nil, err
	}
	adSpt, err := adal.NewServicePrincipalTokenFromCertificate(*oauthConfig, clientID, certificate, privateKey, env.GraphEndpoint)
	if err != nil {
		return nil, err
	}

	return getClient(env, subscriptionID, armSpt, adSpt)
}

func tokenCallback(path string) func(t adal.Token) error {
	return func(token adal.Token) error {
		err := adal.SaveToken(path, 0600, token)
		if err != nil {
			return err
		}
		log.Debugf("Saved token to cache. path=%q", path)
		return nil
	}
}

func tryLoadCachedToken(cachePath string) (*adal.Token, error) {
	log.Debugf("Attempting to load token from cache. path=%q", cachePath)

	// Check for file not found so we can suppress the file not found error
	// LoadToken doesn't discern and returns error either way
	if _, err := os.Stat(cachePath); err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}
		return nil, err
	}

	token, err := adal.LoadToken(cachePath)
	if err != nil {
		return nil, fmt.Errorf("Failed to load token from file: %v", err)
	}

	return token, nil
}

func getOAuthConfig(env azure.Environment, subscriptionID string) (*adal.OAuthConfig, string, error) {
	tenantID, err := acsengine.GetTenantID(env, subscriptionID)
	if err != nil {
		return nil, "", err
	}

	oauthConfig, err := adal.NewOAuthConfig(env.ActiveDirectoryEndpoint, tenantID)
	if err != nil {
		return nil, "", err
	}

	return oauthConfig, tenantID, nil
}

func getClient(env azure.Environment, subscriptionID string, armSpt *adal.ServicePrincipalToken, adSpt *adal.ServicePrincipalToken) (*AzureClient, error) {
	c := &AzureClient{
		environment:           env,
		deploymentsClient:     resources.NewDeploymentsClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID),
		resourcesClient:       resources.NewGroupClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID),
		storageAccountsClient: storage.NewAccountsClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID),
		interfacesClient:      network.NewInterfacesClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID),
		groupsClient:          resources.NewGroupsClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID),
		providersClient:       resources.NewProvidersClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID),
		virtualMachinesClient: compute.NewVirtualMachinesClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID),
	}

	authorizer := autorest.NewBearerAuthorizer(armSpt)
	c.deploymentsClient.Authorizer = authorizer
	c.resourcesClient.Authorizer = authorizer
	c.storageAccountsClient.Authorizer = authorizer
	c.interfacesClient.Authorizer = authorizer
	c.groupsClient.Authorizer = authorizer
	c.providersClient.Authorizer = authorizer
	c.virtualMachinesClient.Authorizer = authorizer

	err := c.ensureProvidersRegistered(subscriptionID)
	if err != nil {
		return nil, err
	}

	return c, nil
}

func (az *AzureClient) ensureProvidersRegistered(subscriptionID string) error {
	registeredProviders, err := az.providersClient.List(to.Int32Ptr(100), "")
	if err != nil {
		return err
	}
	if registeredProviders.Value == nil {
		return fmt.Errorf("Providers list was nil. subscription=%q", subscriptionID)
	}

	m := make(map[string]bool)
	for _, provider := range *registeredProviders.Value {
		m[strings.ToLower(to.String(provider.Namespace))] = to.String(provider.RegistrationState) == "Registered"
	}

	for _, provider := range RequiredResourceProviders {
		registered, ok := m[strings.ToLower(provider)]
		if !ok {
			return fmt.Errorf("Unknown resource provider %q", provider)
		}
		if registered {
			log.Debugf("Already registered for %q", provider)
		} else {
			log.Info("Registering subscription to resource provider. provider=%q subscription=%q", provider, subscriptionID)
			if _, err := az.providersClient.Register(provider); err != nil {
				return err
			}
		}
	}
	return nil
}

func parseRsaPrivateKey(path string) (*rsa.PrivateKey, error) {
	privateKeyData, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}

	block, _ := pem.Decode(privateKeyData)
	if block == nil {
		return nil, fmt.Errorf("Failed to decode a pem block from private key")
	}

	privatePkcs1Key, errPkcs1 := x509.ParsePKCS1PrivateKey(block.Bytes)
	if errPkcs1 == nil {
		return privatePkcs1Key, nil
	}

	privatePkcs8Key, errPkcs8 := x509.ParsePKCS8PrivateKey(block.Bytes)
	if errPkcs8 == nil {
		privatePkcs8RsaKey, ok := privatePkcs8Key.(*rsa.PrivateKey)
		if !ok {
			return nil, fmt.Errorf("pkcs8 contained non-RSA key. Expected RSA key")
		}
		return privatePkcs8RsaKey, nil
	}

	return nil, fmt.Errorf("failed to parse private key as Pkcs#1 or Pkcs#8. (%s). (%s)", errPkcs1, errPkcs8)
}
<commit_msg>drop polling delay to 5 seconds<commit_after>package armhelpers

import (
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/Azure/azure-sdk-for-go/arm/compute"
	"github.com/Azure/azure-sdk-for-go/arm/network"
	"github.com/Azure/azure-sdk-for-go/arm/resources/resources"
	"github.com/Azure/azure-sdk-for-go/arm/resources/subscriptions"
	"github.com/Azure/azure-sdk-for-go/arm/storage"
	"github.com/Azure/go-autorest/autorest"
	"github.com/Azure/go-autorest/autorest/adal"
	"github.com/Azure/go-autorest/autorest/azure"
	"github.com/Azure/go-autorest/autorest/to"
	log "github.com/Sirupsen/logrus"
	"github.com/mitchellh/go-homedir"

	"github.com/Azure/acs-engine/pkg/acsengine"
)

const (
	// AcsEngineClientID is the AAD ClientID for the CLI native application
	AcsEngineClientID = "76e0feec-6b7f-41f0-81a7-b1b944520261"

	// ApplicationDir is the name of the dir where the token is cached
	ApplicationDir = ".acsengine"
)

var (
	// RequiredResourceProviders is the list of Azure Resource Providers needed for ACS-Engine to function
	RequiredResourceProviders = []string{"Microsoft.Compute", "Microsoft.Storage", "Microsoft.Network"}
)

// AzureClient implements the `ACSEngineClient` interface.
// This client is backed by real Azure clients talking to an ARM endpoint.
type AzureClient struct {
	environment azure.Environment

	deploymentsClient     resources.DeploymentsClient
	resourcesClient       resources.GroupClient
	storageAccountsClient storage.AccountsClient
	interfacesClient      network.InterfacesClient
	groupsClient          resources.GroupsClient
	providersClient       resources.ProvidersClient
	subscriptionsClient   subscriptions.GroupClient
	virtualMachinesClient compute.VirtualMachinesClient
}

// NewAzureClientWithDeviceAuth returns an AzureClient by having a user complete a device authentication flow
func NewAzureClientWithDeviceAuth(env azure.Environment, subscriptionID string) (*AzureClient, error) {
	oauthConfig, tenantID, err := getOAuthConfig(env, subscriptionID)
	if err != nil {
		return nil, err
	}

	home, err := homedir.Dir()
	if err != nil {
		return nil, fmt.Errorf("Failed to get user home directory to look for cached token: %q", err)
	}
	cachePath := filepath.Join(home, ApplicationDir, "cache", fmt.Sprintf("%s_%s.token.json", tenantID, AcsEngineClientID))

	rawToken, err := tryLoadCachedToken(cachePath)
	if err != nil {
		return nil, err
	}

	var armSpt *adal.ServicePrincipalToken
	if rawToken != nil {
		armSpt, err = adal.NewServicePrincipalTokenFromManualToken(*oauthConfig, AcsEngineClientID, env.ServiceManagementEndpoint, *rawToken, tokenCallback(cachePath))
		if err != nil {
			return nil, err
		}
		err = armSpt.Refresh()
		if err != nil {
			log.Warnf("Refresh token failed. Will fallback to device auth. %q", err)
		} else {
			adSpt, err := adal.NewServicePrincipalTokenFromManualToken(*oauthConfig, AcsEngineClientID, env.GraphEndpoint, armSpt.Token)
			if err != nil {
				return nil, err
			}
			return getClient(env, subscriptionID, armSpt, adSpt)
		}
	}

	client := &autorest.Client{}

	deviceCode, err := adal.InitiateDeviceAuth(client, *oauthConfig, AcsEngineClientID, env.ServiceManagementEndpoint)
	if err != nil {
		return nil, err
	}
	log.Warnln(*deviceCode.Message)
	deviceToken, err := adal.WaitForUserCompletion(client, deviceCode)
	if err != nil {
		return nil, err
	}

	armSpt, err = adal.NewServicePrincipalTokenFromManualToken(*oauthConfig, AcsEngineClientID, env.ServiceManagementEndpoint, *deviceToken, tokenCallback(cachePath))
	if err != nil {
		return nil, err
	}
	armSpt.Refresh()

	adRawToken := armSpt.Token
	adRawToken.Resource = env.GraphEndpoint
	adSpt, err := adal.NewServicePrincipalTokenFromManualToken(*oauthConfig, AcsEngineClientID, env.GraphEndpoint, adRawToken)
	if err != nil {
		return nil, err
	}

	return getClient(env, subscriptionID, armSpt, adSpt)
}

// NewAzureClientWithClientSecret returns an AzureClient via client_id and client_secret
func NewAzureClientWithClientSecret(env azure.Environment, subscriptionID, clientID, clientSecret string) (*AzureClient, error) {
	oauthConfig, _, err := getOAuthConfig(env, subscriptionID)
	if err != nil {
		return nil, err
	}

	armSpt, err := adal.NewServicePrincipalToken(*oauthConfig, clientID, clientSecret, env.ServiceManagementEndpoint)
	if err != nil {
		return nil, err
	}
	adSpt, err := adal.NewServicePrincipalToken(*oauthConfig, clientID, clientSecret, env.GraphEndpoint)
	if err != nil {
		return nil, err
	}

	return getClient(env, subscriptionID, armSpt, adSpt)
}

// NewAzureClientWithClientCertificate returns an AzureClient via client_id and jwt certificate assertion
func NewAzureClientWithClientCertificate(env azure.Environment, subscriptionID, clientID, certificatePath, privateKeyPath string) (*AzureClient, error) {
	oauthConfig, _, err := getOAuthConfig(env, subscriptionID)
	if err != nil {
		return nil, err
	}

	certificateData, err := ioutil.ReadFile(certificatePath)
	if err != nil {
		return nil, fmt.Errorf("Failed to read certificate: %q", err)
	}

	block, _ := pem.Decode(certificateData)
	if block == nil {
		return nil, fmt.Errorf("Failed to decode pem block from certificate")
	}

	certificate, err := x509.ParseCertificate(block.Bytes)
	if err != nil {
		return nil, fmt.Errorf("Failed to parse certificate: %q", err)
	}

	privateKey, err := parseRsaPrivateKey(privateKeyPath)
	if err != nil {
		return nil, fmt.Errorf("Failed to parse rsa private key: %q", err)
	}

	armSpt, err := adal.NewServicePrincipalTokenFromCertificate(*oauthConfig, clientID, certificate, privateKey, env.ServiceManagementEndpoint)
	if err != nil {
		return nil, err
	}
	adSpt, err := adal.NewServicePrincipalTokenFromCertificate(*oauthConfig, clientID, certificate, privateKey, env.GraphEndpoint)
	if err != nil {
		return nil, err
	}

	return getClient(env, subscriptionID, armSpt, adSpt)
}

func tokenCallback(path string) func(t adal.Token) error {
	return func(token adal.Token) error {
		err := adal.SaveToken(path, 0600, token)
		if err != nil {
			return err
		}
		log.Debugf("Saved token to cache. path=%q", path)
		return nil
	}
}

func tryLoadCachedToken(cachePath string) (*adal.Token, error) {
	log.Debugf("Attempting to load token from cache. path=%q", cachePath)

	// Check for file not found so we can suppress the file not found error
	// LoadToken doesn't discern and returns error either way
	if _, err := os.Stat(cachePath); err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}
		return nil, err
	}

	token, err := adal.LoadToken(cachePath)
	if err != nil {
		return nil, fmt.Errorf("Failed to load token from file: %v", err)
	}

	return token, nil
}

func getOAuthConfig(env azure.Environment, subscriptionID string) (*adal.OAuthConfig, string, error) {
	tenantID, err := acsengine.GetTenantID(env, subscriptionID)
	if err != nil {
		return nil, "", err
	}

	oauthConfig, err := adal.NewOAuthConfig(env.ActiveDirectoryEndpoint, tenantID)
	if err != nil {
		return nil, "", err
	}

	return oauthConfig, tenantID, nil
}

func getClient(env azure.Environment, subscriptionID string, armSpt *adal.ServicePrincipalToken, adSpt *adal.ServicePrincipalToken) (*AzureClient, error) {
	c := &AzureClient{
		environment:           env,
		deploymentsClient:     resources.NewDeploymentsClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID),
		resourcesClient:       resources.NewGroupClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID),
		storageAccountsClient: storage.NewAccountsClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID),
		interfacesClient:      network.NewInterfacesClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID),
		groupsClient:          resources.NewGroupsClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID),
		providersClient:       resources.NewProvidersClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID),
		virtualMachinesClient: compute.NewVirtualMachinesClientWithBaseURI(env.ResourceManagerEndpoint, subscriptionID),
	}

	authorizer := autorest.NewBearerAuthorizer(armSpt)
	c.deploymentsClient.Authorizer = authorizer
	c.resourcesClient.Authorizer = authorizer
	c.storageAccountsClient.Authorizer = authorizer
	c.interfacesClient.Authorizer = authorizer
	c.groupsClient.Authorizer = authorizer
	c.providersClient.Authorizer = authorizer
	c.virtualMachinesClient.Authorizer = authorizer

	c.deploymentsClient.PollingDelay = time.Second * 5

	err := c.ensureProvidersRegistered(subscriptionID)
	if err != nil {
		return nil, err
	}

	return c, nil
}

func (az *AzureClient) ensureProvidersRegistered(subscriptionID string) error {
	registeredProviders, err := az.providersClient.List(to.Int32Ptr(100), "")
	if err != nil {
		return err
	}
	if registeredProviders.Value == nil {
		return fmt.Errorf("Providers list was nil. subscription=%q", subscriptionID)
	}

	m := make(map[string]bool)
	for _, provider := range *registeredProviders.Value {
		m[strings.ToLower(to.String(provider.Namespace))] = to.String(provider.RegistrationState) == "Registered"
	}

	for _, provider := range RequiredResourceProviders {
		registered, ok := m[strings.ToLower(provider)]
		if !ok {
			return fmt.Errorf("Unknown resource provider %q", provider)
		}
		if registered {
			log.Debugf("Already registered for %q", provider)
		} else {
			log.Info("Registering subscription to resource provider. provider=%q subscription=%q", provider, subscriptionID)
			if _, err := az.providersClient.Register(provider); err != nil {
				return err
			}
		}
	}
	return nil
}

func parseRsaPrivateKey(path string) (*rsa.PrivateKey, error) {
	privateKeyData, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}

	block, _ := pem.Decode(privateKeyData)
	if block == nil {
		return nil, fmt.Errorf("Failed to decode a pem block from private key")
	}

	privatePkcs1Key, errPkcs1 := x509.ParsePKCS1PrivateKey(block.Bytes)
	if errPkcs1 == nil {
		return privatePkcs1Key, nil
	}

	privatePkcs8Key, errPkcs8 := x509.ParsePKCS8PrivateKey(block.Bytes)
	if errPkcs8 == nil {
		privatePkcs8RsaKey, ok := privatePkcs8Key.(*rsa.PrivateKey)
		if !ok {
			return nil, fmt.Errorf("pkcs8 contained non-RSA key. Expected RSA key")
		}
		return privatePkcs8RsaKey, nil
	}

	return nil, fmt.Errorf("failed to parse private key as Pkcs#1 or Pkcs#8. (%s). (%s)", errPkcs1, errPkcs8)
}
<|endoftext|>