<commit_before>/*
Copyright 2020 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package common

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"regexp"
	"strings"
	"time"

	"github.com/sirupsen/logrus"
	"k8s.io/apimachinery/pkg/util/sets"

	prowv1 "k8s.io/test-infra/prow/apis/prowjobs/v1"
	"k8s.io/test-infra/prow/config"
	"k8s.io/test-infra/prow/io/providers"
	"k8s.io/test-infra/prow/spyglass/api"
)

var lensTemplate = template.Must(template.New("sg").Parse(string(MustAsset("static/spyglass-lens.html"))))
var buildLogRegex = regexp.MustCompile(`^(?:[^/]*-)?build-log\.txt$`)

type LensWithConfiguration struct {
	Config LensOpt
	Lens   api.Lens
}

func NewLensServer(
	listenAddress string,
	pjFetcher ProwJobFetcher,
	storageArtifactFetcher ArtifactFetcher,
	podLogArtifactFetcher ArtifactFetcher,
	cfg config.Getter,
	lenses []LensWithConfiguration,
) (*http.Server, error) {

	mux := http.NewServeMux()

	seenLens := sets.String{}
	for _, lens := range lenses {
		if seenLens.Has(lens.Config.LensName) {
			return nil, fmt.Errorf("duplicate lens named %q", lens.Config.LensName)
		}
		seenLens.Insert(lens.Config.LensName)

		logrus.WithField("Lens", lens.Config.LensName).Info("Adding handler for lens")
		opt := lensHandlerOpts{
			PJFetcher:              pjFetcher,
			StorageArtifactFetcher: storageArtifactFetcher,
			PodLogArtifactFetcher:  podLogArtifactFetcher,
			ConfigGetter:           cfg,
			LensOpt:                lens.Config,
		}
		mux.Handle(DyanmicPathForLens(lens.Config.LensName), newLensHandler(lens.Lens, opt))
	}
	mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		logrus.WithField("path", r.URL.Path).Error("LensServer got request on unhandled path")
		http.NotFound(w, r)
	}))

	return &http.Server{Addr: listenAddress, Handler: mux}, nil
}

type LensOpt struct {
	LensResourcesDir string
	LensName         string
	LensTitle        string
}

type lensHandlerOpts struct {
	PJFetcher              ProwJobFetcher
	StorageArtifactFetcher ArtifactFetcher
	PodLogArtifactFetcher  ArtifactFetcher
	ConfigGetter           config.Getter
	LensOpt
}

func newLensHandler(lens api.Lens, opts lensHandlerOpts) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			writeHTTPError(w, fmt.Errorf("failed to read request body: %w", err), http.StatusInternalServerError)
			return
		}

		request := &api.LensRequest{}
		if err := json.Unmarshal(body, request); err != nil {
			writeHTTPError(w, fmt.Errorf("failed to unmarshal request: %w", err), http.StatusBadRequest)
			return
		}

		artifacts, err := FetchArtifacts(r.Context(), opts.PJFetcher, opts.ConfigGetter, opts.StorageArtifactFetcher, opts.PodLogArtifactFetcher, request.ArtifactSource, "", opts.ConfigGetter().Deck.Spyglass.SizeLimit, request.Artifacts)
		if err != nil || len(artifacts) == 0 {
			statusCode := http.StatusInternalServerError
			if len(artifacts) == 0 {
				statusCode = http.StatusNotFound
				err = errors.New("no artifacts found")
			}

			writeHTTPError(w, fmt.Errorf("failed to retrieve expected artifacts: %w", err), statusCode)
			return
		}

		switch request.Action {
		case api.RequestActionInitial:
			w.Header().Set("Content-Type", "text/html; encoding=utf-8")
			lensTemplate.Execute(w, struct {
				Title   string
				BaseURL string
				Head    template.HTML
				Body    template.HTML
			}{
				opts.LensTitle,
				request.ResourceRoot,
				template.HTML(lens.Header(artifacts, opts.LensResourcesDir, opts.ConfigGetter().Deck.Spyglass.Lenses[request.LensIndex].Lens.Config)),
				template.HTML(lens.Body(artifacts, opts.LensResourcesDir, "", opts.ConfigGetter().Deck.Spyglass.Lenses[request.LensIndex].Lens.Config)),
			})

		case api.RequestActionRerender:
			w.Header().Set("Content-Type", "text/html; encoding=utf-8")
			w.Write([]byte(lens.Body(artifacts, opts.LensResourcesDir, request.Data, opts.ConfigGetter().Deck.Spyglass.Lenses[request.LensIndex].Lens.Config)))

		case api.RequestActionCallBack:
			w.Write([]byte(lens.Callback(artifacts, opts.LensResourcesDir, request.Data, opts.ConfigGetter().Deck.Spyglass.Lenses[request.LensIndex].Lens.Config)))

		default:
			w.WriteHeader(http.StatusBadRequest)
			// This is a bit weird as we proxy this and the request we are complaining about was issued by Deck, not by the original client that sees this error
			w.Write([]byte(fmt.Sprintf("Invalid action %q", request.Action)))
		}
	}
}

func writeHTTPError(w http.ResponseWriter, err error, statusCode int) {
	if statusCode == 0 {
		statusCode = http.StatusInternalServerError
	}
	logrus.WithError(err).WithField("statusCode", statusCode).Debug("Failed to process request")
	w.WriteHeader(statusCode)
	if _, err := w.Write([]byte(err.Error())); err != nil {
		logrus.WithError(err).Error("Failed to write response")
	}
}

// ArtifactFetcher knows how to fetch artifacts
type ArtifactFetcher interface {
	Artifact(ctx context.Context, key string, artifactName string, sizeLimit int64) (api.Artifact, error)
}

// FetchArtifacts fetches artifacts.
// TODO: Unexport once we only have remote lenses
func FetchArtifacts(
	ctx context.Context,
	pjFetcher ProwJobFetcher,
	cfg config.Getter,
	storageArtifactFetcher ArtifactFetcher,
	podLogArtifactFetcher ArtifactFetcher,
	src string,
	podName string,
	sizeLimit int64,
	artifactNames []string,
) ([]api.Artifact, error) {
	artStart := time.Now()
	arts := []api.Artifact{}
	keyType, key, err := splitSrc(src)
	if err != nil {
		return arts, fmt.Errorf("error parsing src: %v", err)
	}
	gcsKey := ""
	switch keyType {
	case api.ProwKeyType:
		storageProvider, key, err := ProwToGCS(pjFetcher, cfg, key)
		if err != nil {
			logrus.Warningln(err)
		}
		gcsKey = fmt.Sprintf("%s://%s", storageProvider, strings.TrimSuffix(key, "/"))
	default:
		if keyType == api.GCSKeyType {
			keyType = providers.GS
		}
		gcsKey = fmt.Sprintf("%s://%s", keyType, strings.TrimSuffix(key, "/"))
	}

	logsNeeded := []string{}

	for _, name := range artifactNames {
		art, err := storageArtifactFetcher.Artifact(ctx, gcsKey, name, sizeLimit)
		if err == nil {
			// Actually try making a request, because calling StorageArtifactFetcher.artifact does no I/O.
			// (these files are being explicitly requested and so will presumably soon be accessed, so
			// the extra network I/O should not be too problematic).
			_, err = art.Size()
		}
		if err != nil {
			if buildLogRegex.MatchString(name) {
				logsNeeded = append(logsNeeded, name)
			}
			continue
		}
		arts = append(arts, art)
	}

	for _, logName := range logsNeeded {
		art, err := podLogArtifactFetcher.Artifact(ctx, src, logName, sizeLimit)
		if err != nil {
			logrus.Errorf("Failed to fetch pod log: %v", err)
		} else {
			arts = append(arts, art)
		}
	}

	logrus.WithField("duration", time.Since(artStart).String()).Infof("Retrieved artifacts for %v", src)
	return arts, nil
}

// ProwJobFetcher knows how to get a ProwJob
type ProwJobFetcher interface {
	GetProwJob(job string, id string) (prowv1.ProwJob, error)
}

// prowToGCS returns the GCS key corresponding to the given prow key
// TODO: Unexport once we only have remote lenses
func ProwToGCS(fetcher ProwJobFetcher, config config.Getter, prowKey string) (string, string, error) {
	jobName, buildID, err := KeyToJob(prowKey)
	if err != nil {
		return "", "", fmt.Errorf("could not get GCS src: %v", err)
	}

	job, err := fetcher.GetProwJob(jobName, buildID)
	if err != nil {
		return "", "", fmt.Errorf("failed to get prow job from src %q: %v", prowKey, err)
	}

	url := job.Status.URL
	prefix := config().Plank.GetJobURLPrefix(job.Spec.Refs)
	if !strings.HasPrefix(url, prefix) {
		return "", "", fmt.Errorf("unexpected job URL %q when finding GCS path: expected something starting with %q", url, prefix)
	}

	// example:
	// * url: https://prow.k8s.io/view/gs/kubernetes-jenkins/logs/ci-benchmark-microbenchmarks/1258197944759226371
	// * prefix: https://prow.k8s.io/view/
	// * storagePath: gs/kubernetes-jenkins/logs/ci-benchmark-microbenchmarks/1258197944759226371
	storagePath := strings.TrimPrefix(url, prefix)
	if strings.HasPrefix(storagePath, api.GCSKeyType) {
		storagePath = strings.Replace(storagePath, api.GCSKeyType, providers.GS, 1)
	}
	storagePathWithoutProvider := storagePath
	storagePathSegments := strings.SplitN(storagePath, "/", 2)
	if providers.HasStorageProviderPrefix(storagePath) {
		storagePathWithoutProvider = storagePathSegments[1]
	}

	// try to parse storageProvider from DecorationConfig.GCSConfiguration.Bucket
	// if it doesn't work fallback to URL parsing
	if job.Spec.DecorationConfig != nil && job.Spec.DecorationConfig.GCSConfiguration != nil {
		prowPath, err := prowv1.ParsePath(job.Spec.DecorationConfig.GCSConfiguration.Bucket)
		if err == nil {
			return prowPath.StorageProvider(), storagePathWithoutProvider, nil
		}
		logrus.Warnf("Could not parse storageProvider from DecorationConfig.GCSConfiguration.Bucket = %s: %v", job.Spec.DecorationConfig.GCSConfiguration.Bucket, err)
	}

	return storagePathSegments[0], storagePathWithoutProvider, nil
}

func splitSrc(src string) (keyType, key string, err error) {
	split := strings.SplitN(src, "/", 2)
	if len(split) < 2 {
		err = fmt.Errorf("invalid src %s: expected <key-type>/<key>", src)
		return
	}
	keyType = split[0]
	key = split[1]
	return
}

// keyToJob takes a spyglass URL and returns the jobName and buildID.
func KeyToJob(src string) (jobName string, buildID string, err error) {
	src = strings.Trim(src, "/")
	parsed := strings.Split(src, "/")
	if len(parsed) < 2 {
		return "", "", fmt.Errorf("expected at least two path components in %q", src)
	}
	jobName = parsed[len(parsed)-2]
	buildID = parsed[len(parsed)-1]
	return jobName, buildID, nil
}

const prefixSpyglassDynamicHandlers = "dynamic"

func DyanmicPathForLens(lensName string) string {
	return fmt.Sprintf("/%s/%s", prefixSpyglassDynamicHandlers, lensName)
}
<commit_msg>Spyglass: Log an error when artifact fetching fails<commit_after>/*
Copyright 2020 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package common

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"regexp"
	"strings"
	"time"

	"github.com/sirupsen/logrus"
	"k8s.io/apimachinery/pkg/util/sets"

	prowv1 "k8s.io/test-infra/prow/apis/prowjobs/v1"
	"k8s.io/test-infra/prow/config"
	"k8s.io/test-infra/prow/io/providers"
	"k8s.io/test-infra/prow/spyglass/api"
)

var lensTemplate = template.Must(template.New("sg").Parse(string(MustAsset("static/spyglass-lens.html"))))
var buildLogRegex = regexp.MustCompile(`^(?:[^/]*-)?build-log\.txt$`)

type LensWithConfiguration struct {
	Config LensOpt
	Lens   api.Lens
}

func NewLensServer(
	listenAddress string,
	pjFetcher ProwJobFetcher,
	storageArtifactFetcher ArtifactFetcher,
	podLogArtifactFetcher ArtifactFetcher,
	cfg config.Getter,
	lenses []LensWithConfiguration,
) (*http.Server, error) {

	mux := http.NewServeMux()

	seenLens := sets.String{}
	for _, lens := range lenses {
		if seenLens.Has(lens.Config.LensName) {
			return nil, fmt.Errorf("duplicate lens named %q", lens.Config.LensName)
		}
		seenLens.Insert(lens.Config.LensName)

		logrus.WithField("Lens", lens.Config.LensName).Info("Adding handler for lens")
		opt := lensHandlerOpts{
			PJFetcher:              pjFetcher,
			StorageArtifactFetcher: storageArtifactFetcher,
			PodLogArtifactFetcher:  podLogArtifactFetcher,
			ConfigGetter:           cfg,
			LensOpt:                lens.Config,
		}
		mux.Handle(DyanmicPathForLens(lens.Config.LensName), newLensHandler(lens.Lens, opt))
	}
	mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		logrus.WithField("path", r.URL.Path).Error("LensServer got request on unhandled path")
		http.NotFound(w, r)
	}))

	return &http.Server{Addr: listenAddress, Handler: mux}, nil
}

type LensOpt struct {
	LensResourcesDir string
	LensName         string
	LensTitle        string
}

type lensHandlerOpts struct {
	PJFetcher              ProwJobFetcher
	StorageArtifactFetcher ArtifactFetcher
	PodLogArtifactFetcher  ArtifactFetcher
	ConfigGetter           config.Getter
	LensOpt
}

func newLensHandler(lens api.Lens, opts lensHandlerOpts) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			writeHTTPError(w, fmt.Errorf("failed to read request body: %w", err), http.StatusInternalServerError)
			return
		}

		request := &api.LensRequest{}
		if err := json.Unmarshal(body, request); err != nil {
			writeHTTPError(w, fmt.Errorf("failed to unmarshal request: %w", err), http.StatusBadRequest)
			return
		}

		artifacts, err := FetchArtifacts(r.Context(), opts.PJFetcher, opts.ConfigGetter, opts.StorageArtifactFetcher, opts.PodLogArtifactFetcher, request.ArtifactSource, "", opts.ConfigGetter().Deck.Spyglass.SizeLimit, request.Artifacts)
		if err != nil || len(artifacts) == 0 {
			statusCode := http.StatusInternalServerError
			if len(artifacts) == 0 {
				statusCode = http.StatusNotFound
				err = errors.New("no artifacts found")
			}

			writeHTTPError(w, fmt.Errorf("failed to retrieve expected artifacts: %w", err), statusCode)
			return
		}

		switch request.Action {
		case api.RequestActionInitial:
			w.Header().Set("Content-Type", "text/html; encoding=utf-8")
			lensTemplate.Execute(w, struct {
				Title   string
				BaseURL string
				Head    template.HTML
				Body    template.HTML
			}{
				opts.LensTitle,
				request.ResourceRoot,
				template.HTML(lens.Header(artifacts, opts.LensResourcesDir, opts.ConfigGetter().Deck.Spyglass.Lenses[request.LensIndex].Lens.Config)),
				template.HTML(lens.Body(artifacts, opts.LensResourcesDir, "", opts.ConfigGetter().Deck.Spyglass.Lenses[request.LensIndex].Lens.Config)),
			})

		case api.RequestActionRerender:
			w.Header().Set("Content-Type", "text/html; encoding=utf-8")
			w.Write([]byte(lens.Body(artifacts, opts.LensResourcesDir, request.Data, opts.ConfigGetter().Deck.Spyglass.Lenses[request.LensIndex].Lens.Config)))

		case api.RequestActionCallBack:
			w.Write([]byte(lens.Callback(artifacts, opts.LensResourcesDir, request.Data, opts.ConfigGetter().Deck.Spyglass.Lenses[request.LensIndex].Lens.Config)))

		default:
			w.WriteHeader(http.StatusBadRequest)
			// This is a bit weird as we proxy this and the request we are complaining about was issued by Deck, not by the original client that sees this error
			w.Write([]byte(fmt.Sprintf("Invalid action %q", request.Action)))
		}
	}
}

func writeHTTPError(w http.ResponseWriter, err error, statusCode int) {
	if statusCode == 0 {
		statusCode = http.StatusInternalServerError
	}
	logrus.WithError(err).WithField("statusCode", statusCode).Debug("Failed to process request")
	w.WriteHeader(statusCode)
	if _, err := w.Write([]byte(err.Error())); err != nil {
		logrus.WithError(err).Error("Failed to write response")
	}
}

// ArtifactFetcher knows how to fetch artifacts
type ArtifactFetcher interface {
	Artifact(ctx context.Context, key string, artifactName string, sizeLimit int64) (api.Artifact, error)
}

// FetchArtifacts fetches artifacts.
// TODO: Unexport once we only have remote lenses
func FetchArtifacts(
	ctx context.Context,
	pjFetcher ProwJobFetcher,
	cfg config.Getter,
	storageArtifactFetcher ArtifactFetcher,
	podLogArtifactFetcher ArtifactFetcher,
	src string,
	podName string,
	sizeLimit int64,
	artifactNames []string,
) ([]api.Artifact, error) {
	artStart := time.Now()
	arts := []api.Artifact{}
	keyType, key, err := splitSrc(src)
	if err != nil {
		return arts, fmt.Errorf("error parsing src: %v", err)
	}
	gcsKey := ""
	switch keyType {
	case api.ProwKeyType:
		storageProvider, key, err := ProwToGCS(pjFetcher, cfg, key)
		if err != nil {
			logrus.Warningln(err)
		}
		gcsKey = fmt.Sprintf("%s://%s", storageProvider, strings.TrimSuffix(key, "/"))
	default:
		if keyType == api.GCSKeyType {
			keyType = providers.GS
		}
		gcsKey = fmt.Sprintf("%s://%s", keyType, strings.TrimSuffix(key, "/"))
	}

	logsNeeded := []string{}

	for _, name := range artifactNames {
		art, err := storageArtifactFetcher.Artifact(ctx, gcsKey, name, sizeLimit)
		if err == nil {
			// Actually try making a request, because calling StorageArtifactFetcher.artifact does no I/O.
			// (these files are being explicitly requested and so will presumably soon be accessed, so
			// the extra network I/O should not be too problematic).
			_, err = art.Size()
		}
		if err != nil {
			if buildLogRegex.MatchString(name) {
				logsNeeded = append(logsNeeded, name)
			}
			logrus.WithError(err).WithField("artifact", name).Error("Failed to fetch artifact")
			continue
		}
		arts = append(arts, art)
	}

	for _, logName := range logsNeeded {
		art, err := podLogArtifactFetcher.Artifact(ctx, src, logName, sizeLimit)
		if err != nil {
			logrus.Errorf("Failed to fetch pod log: %v", err)
		} else {
			arts = append(arts, art)
		}
	}

	logrus.WithField("duration", time.Since(artStart).String()).Infof("Retrieved artifacts for %v", src)
	return arts, nil
}

// ProwJobFetcher knows how to get a ProwJob
type ProwJobFetcher interface {
	GetProwJob(job string, id string) (prowv1.ProwJob, error)
}

// prowToGCS returns the GCS key corresponding to the given prow key
// TODO: Unexport once we only have remote lenses
func ProwToGCS(fetcher ProwJobFetcher, config config.Getter, prowKey string) (string, string, error) {
	jobName, buildID, err := KeyToJob(prowKey)
	if err != nil {
		return "", "", fmt.Errorf("could not get GCS src: %v", err)
	}

	job, err := fetcher.GetProwJob(jobName, buildID)
	if err != nil {
		return "", "", fmt.Errorf("failed to get prow job from src %q: %v", prowKey, err)
	}

	url := job.Status.URL
	prefix := config().Plank.GetJobURLPrefix(job.Spec.Refs)
	if !strings.HasPrefix(url, prefix) {
		return "", "", fmt.Errorf("unexpected job URL %q when finding GCS path: expected something starting with %q", url, prefix)
	}

	// example:
	// * url: https://prow.k8s.io/view/gs/kubernetes-jenkins/logs/ci-benchmark-microbenchmarks/1258197944759226371
	// * prefix: https://prow.k8s.io/view/
	// * storagePath: gs/kubernetes-jenkins/logs/ci-benchmark-microbenchmarks/1258197944759226371
	storagePath := strings.TrimPrefix(url, prefix)
	if strings.HasPrefix(storagePath, api.GCSKeyType) {
		storagePath = strings.Replace(storagePath, api.GCSKeyType, providers.GS, 1)
	}
	storagePathWithoutProvider := storagePath
	storagePathSegments := strings.SplitN(storagePath, "/", 2)
	if providers.HasStorageProviderPrefix(storagePath) {
		storagePathWithoutProvider = storagePathSegments[1]
	}

	// try to parse storageProvider from DecorationConfig.GCSConfiguration.Bucket
	// if it doesn't work fallback to URL parsing
	if job.Spec.DecorationConfig != nil && job.Spec.DecorationConfig.GCSConfiguration != nil {
		prowPath, err := prowv1.ParsePath(job.Spec.DecorationConfig.GCSConfiguration.Bucket)
		if err == nil {
			return prowPath.StorageProvider(), storagePathWithoutProvider, nil
		}
		logrus.Warnf("Could not parse storageProvider from DecorationConfig.GCSConfiguration.Bucket = %s: %v", job.Spec.DecorationConfig.GCSConfiguration.Bucket, err)
	}

	return storagePathSegments[0], storagePathWithoutProvider, nil
}

func splitSrc(src string) (keyType, key string, err error) {
	split := strings.SplitN(src, "/", 2)
	if len(split) < 2 {
		err = fmt.Errorf("invalid src %s: expected <key-type>/<key>", src)
		return
	}
	keyType = split[0]
	key = split[1]
	return
}

// keyToJob takes a spyglass URL and returns the jobName and buildID.
func KeyToJob(src string) (jobName string, buildID string, err error) {
	src = strings.Trim(src, "/")
	parsed := strings.Split(src, "/")
	if len(parsed) < 2 {
		return "", "", fmt.Errorf("expected at least two path components in %q", src)
	}
	jobName = parsed[len(parsed)-2]
	buildID = parsed[len(parsed)-1]
	return jobName, buildID, nil
}

const prefixSpyglassDynamicHandlers = "dynamic"

func DyanmicPathForLens(lensName string) string {
	return fmt.Sprintf("/%s/%s", prefixSpyglassDynamicHandlers, lensName)
}
<|endoftext|>


<commit_before><commit_msg>Removed old hot fix on eof<commit_after><|endoftext|>


<commit_before>package client

import "github.com/MustWin/baremetal-sdk-go"

type BareMetalClient interface {
	CreateUser(name, description string, options ...baremetal.Options) (*baremetal.IdentityResource, error)
	GetUser(userID string) (*baremetal.IdentityResource, error)
	UpdateUser(userID, userDescription string, opts ...baremetal.Options) (*baremetal.IdentityResource, error)
	DeleteUser(userID string, opts ...baremetal.Options) error

	CreateGroup(name, description string, options ...baremetal.Options) (*baremetal.IdentityResource, error)
	GetGroup(userID string) (*baremetal.IdentityResource, error)
	UpdateGroup(userID, userDescription string, opts ...baremetal.Options) (*baremetal.IdentityResource, error)
	DeleteGroup(userID string, opts ...baremetal.Options) error

	CreatePolicy(name, description string, statements []string, opts ...baremetal.Options) (*baremetal.Policy, error)
	GetPolicy(id string) (*baremetal.Policy, error)
	UpdatePolicy(id, description string, statements []string, opts ...baremetal.Options) (*baremetal.Policy, error)
	DeletePolicy(id string, opts ...baremetal.Options) error

	CreateCompartment(name, description string, options ...baremetal.Options) (*baremetal.IdentityResource, error)
	GetCompartment(userID string) (*baremetal.IdentityResource, error)
	UpdateCompartment(userID, userDescription string, opts ...baremetal.Options) (*baremetal.IdentityResource, error)

	ListShapes(compartmentID string, opt ...baremetal.Options) (*baremetal.ListShapes, error)
	ListVnicAttachments(compartmentID string, opt ...baremetal.Options) (*baremetal.ListVnicAttachments, error)

	CreateCpe(compartmentID, displayName, IPAddress string, opts ...baremetal.Options) (cpe *baremetal.Cpe, e error)
	GetCpe(id string, opts ...baremetal.Options) (cpe *baremetal.Cpe, e error)
	DeleteCpe(id string, opts ...baremetal.Options) (e error)
	ListCpes(compartmentID string, opts ...baremetal.Options) (cpes *baremetal.ListCpes, e error)

	CreateVolume(availabiltyDomain, compartmentID string, opts ...baremetal.Options) (vol *baremetal.Volume, e error)
	GetVolume(id string, opts ...baremetal.Options) (vol *baremetal.Volume, e error)
	UpdateVolume(id string, opts ...baremetal.Options) (vol *baremetal.Volume, e error)
	DeleteVolume(id string, opts ...baremetal.Options) (e error)
	ListVolumes(compartmentID string, opts ...baremetal.Options) (vols *baremetal.ListVolumes, e error)

	LaunchInstance(availabilityDomain, compartmentID, image, shape, subnetID string, metadata map[string]string, opts ...baremetal.Options) (inst *baremetal.Instance, e error)
	GetInstance(instanceID string) (inst *baremetal.Instance, e error)
	UpdateInstance(instanceID string, opts ...baremetal.Options) (inst *baremetal.Instance, e error)
	TerminateInstance(instanceID string, opts ...baremetal.Options) (e error)
	ListInstances(compartmentID string, opts ...baremetal.Options) (list *baremetal.ListInstances, e error)

	AttachVolume(compartmentID, instanceID, attachmentType, volumeID string, opts ...baremetal.Options) (vol *baremetal.VolumeAttachment, e error)
	GetVolumeAttachment(id string, opts ...baremetal.Options) (vol *baremetal.VolumeAttachment, e error)
	DetachVolume(id string, opts ...baremetal.Options) (e error)
	ListVolumeAttachments(compartmentID string, opts ...baremetal.Options) (res *baremetal.ListVolumeAttachments, e error)

	CreateSubnet(availabilityDomain, cidrBlock, compartmentID, routeTableID, vcnID string, securityListIDs []string, opts ...baremetal.Options) (*baremetal.Subnet, error)
	GetSubnet(subnetID string) (sn *baremetal.Subnet, e error)
	ListSubnets(compartmentID, vcnID string, opts ...baremetal.Options) (*baremetal.ListSubnets, error)
	DeleteSubnet(subnetID string, opts ...baremetal.Options) error

	CreateVirtualNetwork(cidrBlock, compartmentID string, opts ...baremetal.Options) (*baremetal.VirtualNetwork, error)
	GetVirtualNetwork(id string, opts ...baremetal.Options) (vcn *baremetal.VirtualNetwork, e error)
	DeleteVirtualNetwork(id string, opts ...baremetal.Options) error
	ListVirtualNetworks(compartmentID string, opts ...baremetal.Options) (*baremetal.ListVirtualNetworks, error)

	CreateIPSecConnection(compartmentID, cpeID, drgID string, staticRoutes []string, opts ...baremetal.Options) (conn *baremetal.IPSecConnection, e error)
	ListIPSecConnections(compartmentID string, opts ...baremetal.Options) (conns *baremetal.ListIPSecConnections, e error)
	GetIPSecConnection(id string) (conn *baremetal.IPSecConnection, e error)
	DeleteIPSecConnection(id string, opts ...baremetal.Options) (e error)
	GetIPSecConnectionDeviceStatus(id string) (status *baremetal.IPSecConnectionDeviceStatus, e error)
	GetIPSecConnectionDeviceConfig(id string) (status *baremetal.IPSecConnectionDeviceConfig, e error)

	CreateDrg(compartmentID string, opts ...baremetal.Options) (*baremetal.Drg, error)
	GetDrg(id string, opts ...baremetal.Options) (*baremetal.Drg, error)
	DeleteDrg(id string, opts ...baremetal.Options) error
	ListDrgs(compartmentID string, opts ...baremetal.Options) (*baremetal.ListDrgs, error)

	CreateDrgAttachment(compartmentID, drgID, vcnID string, opts ...baremetal.Options) (vol *baremetal.DrgAttachment, e error)
	GetDrgAttachment(id string, opts ...baremetal.Options) (vol *baremetal.DrgAttachment, e error)
	DeleteDrgAttachment(id string, opts ...baremetal.Options) (e error)
	ListDrgAttachments(compartmentID string, opts ...baremetal.Options) (res *baremetal.ListDrgAttachments, e error)

	CreateInternetGateway(compartmentID, vcnID string, isEnabled bool, opts ...baremetal.Options) (gw *baremetal.InternetGateway, e error)
	GetInternetGateway(id string) (gw *baremetal.InternetGateway, e error)
	UpdateInternetGateway(id string, isEnabled bool, opts ...baremetal.Options) (gw *baremetal.InternetGateway, e error)
	DeleteInternetGateway(id string, opts ...baremetal.Options) (e error)
	ListInternetGateways(compartmentID, vcnID string, opts ...baremetal.Options) (l *baremetal.ListInternetGateways, e error)

	CreateRouteTable(compartmentID, vcnID string, routeRules []baremetal.RouteRule, opts ...baremetal.Options) (res *baremetal.RouteTable, e error)
	GetRouteTable(id string, opts ...baremetal.Options) (vol *baremetal.RouteTable, e error)
	UpdateRouteTable(id string, routeRules []baremetal.RouteRule, opts ...baremetal.Options) (res *baremetal.RouteTable, e error)
	DeleteRouteTable(id string, opts ...baremetal.Options) (e error)
	ListRouteTables(compartmentID, vcnID string, opts ...baremetal.Options) (vols *baremetal.ListRouteTables, e error)

	CaptureConsoleHistory(instanceID string, opts ...baremetal.Options) (icHistory *baremetal.ConsoleHistoryMetadata, e error)
	GetConsoleHistory(id string, opts ...baremetal.Options) (icHistory *baremetal.ConsoleHistoryMetadata, e error)
	DeleteConsoleHistory(id string) (e error)

	GetVnic(vnicID string) (vnic *baremetal.Vnic, e error)

	CreateOrResetUIPassword(userID string, opts ...baremetal.Options) (resource *baremetal.UIPassword, e error)

	CreateVolumeBackup(volumeID string, opts ...baremetal.Options) (vol *baremetal.VolumeBackup, e error)
	GetVolumeBackup(id string, opts ...baremetal.Options) (vol *baremetal.VolumeBackup, e error)
	UpdateVolumeBackup(id string, opts ...baremetal.Options) (vol *baremetal.VolumeBackup, e error)
	DeleteVolumeBackup(id string, opts ...baremetal.Options) (e error)
}
<commit_msg>Added VNIC datasource<commit_after>package client

import "github.com/MustWin/baremetal-sdk-go"

type BareMetalClient interface {
	CreateUser(name, description string, options ...baremetal.Options) (*baremetal.IdentityResource, error)
	GetUser(userID string) (*baremetal.IdentityResource, error)
	UpdateUser(userID, userDescription string, opts ...baremetal.Options) (*baremetal.IdentityResource, error)
	DeleteUser(userID string, opts ...baremetal.Options) error

	CreateGroup(name, description string, options ...baremetal.Options) (*baremetal.IdentityResource, error)
	GetGroup(userID string) (*baremetal.IdentityResource, error)
	UpdateGroup(userID, userDescription string, opts ...baremetal.Options) (*baremetal.IdentityResource, error)
	DeleteGroup(userID string, opts ...baremetal.Options) error

	CreatePolicy(name, description string, statements []string, opts ...baremetal.Options) (*baremetal.Policy, error)
	GetPolicy(id string) (*baremetal.Policy, error)
	UpdatePolicy(id, description string, statements []string, opts ...baremetal.Options) (*baremetal.Policy, error)
	DeletePolicy(id string, opts ...baremetal.Options) error

	CreateCompartment(name, description string, options ...baremetal.Options) (*baremetal.IdentityResource, error)
	GetCompartment(userID string) (*baremetal.IdentityResource, error)
	UpdateCompartment(userID, userDescription string, opts ...baremetal.Options) (*baremetal.IdentityResource, error)

	ListShapes(compartmentID string, opt ...baremetal.Options) (*baremetal.ListShapes, error)
	ListVnicAttachments(compartmentID string, opt ...baremetal.Options) (*baremetal.ListVnicAttachments, error)

	CreateCpe(compartmentID, displayName, IPAddress string, opts ...baremetal.Options) (cpe *baremetal.Cpe, e error)
	GetCpe(id string, opts ...baremetal.Options) (cpe *baremetal.Cpe, e error)
	DeleteCpe(id string, opts ...baremetal.Options) (e error)
	ListCpes(compartmentID string, opts ...baremetal.Options) (cpes *baremetal.ListCpes, e error)

	CreateVolume(availabiltyDomain, compartmentID string, opts ...baremetal.Options) (vol *baremetal.Volume, e error)
	GetVolume(id string, opts ...baremetal.Options) (vol *baremetal.Volume, e error)
	UpdateVolume(id string, opts ...baremetal.Options) (vol *baremetal.Volume, e error)
	DeleteVolume(id string, opts ...baremetal.Options) (e error)
	ListVolumes(compartmentID string, opts ...baremetal.Options) (vols *baremetal.ListVolumes, e error)

	LaunchInstance(availabilityDomain, compartmentID, image, shape, subnetID string, metadata map[string]string, opts ...baremetal.Options) (inst *baremetal.Instance, e error)
	GetInstance(instanceID string) (inst *baremetal.Instance, e error)
	UpdateInstance(instanceID string, opts ...baremetal.Options) (inst *baremetal.Instance, e error)
	TerminateInstance(instanceID string, opts ...baremetal.Options) (e error)
	ListInstances(compartmentID string, opts ...baremetal.Options) (list *baremetal.ListInstances, e error)

	AttachVolume(compartmentID, instanceID, attachmentType, volumeID string, opts ...baremetal.Options) (vol *baremetal.VolumeAttachment, e error)
	GetVolumeAttachment(id string, opts ...baremetal.Options) (vol *baremetal.VolumeAttachment, e error)
	DetachVolume(id string, opts ...baremetal.Options) (e error)
	ListVolumeAttachments(compartmentID string, opts ...baremetal.Options) (res *baremetal.ListVolumeAttachments, e error)

	CreateSubnet(availabilityDomain, cidrBlock, compartmentID, routeTableID, vcnID string, securityListIDs []string, opts ...baremetal.Options) (*baremetal.Subnet, error)
	GetSubnet(subnetID string) (sn *baremetal.Subnet, e error)
	ListSubnets(compartmentID, vcnID string, opts ...baremetal.Options) (*baremetal.ListSubnets, error)
	DeleteSubnet(subnetID string, opts ...baremetal.Options) error

	CreateVirtualNetwork(cidrBlock, compartmentID string, opts ...baremetal.Options) (*baremetal.VirtualNetwork, error)
	GetVirtualNetwork(id string, opts ...baremetal.Options) (vcn *baremetal.VirtualNetwork, e error)
	DeleteVirtualNetwork(id string, opts ...baremetal.Options) error
	ListVirtualNetworks(compartmentID string, opts ...baremetal.Options) (*baremetal.ListVirtualNetworks, error)

	CreateIPSecConnection(compartmentID, cpeID, drgID string, staticRoutes []string, opts ...baremetal.Options) (conn *baremetal.IPSecConnection, e error)
	ListIPSecConnections(compartmentID string, opts ...baremetal.Options) (conns *baremetal.ListIPSecConnections, e error)
	GetIPSecConnection(id string) (conn *baremetal.IPSecConnection, e error)
	DeleteIPSecConnection(id string, opts ...baremetal.Options) (e error)
	GetIPSecConnectionDeviceStatus(id string) (status *baremetal.IPSecConnectionDeviceStatus, e error)
	GetIPSecConnectionDeviceConfig(id string) (status *baremetal.IPSecConnectionDeviceConfig, e error)

	CreateDrg(compartmentID string, opts ...baremetal.Options) (*baremetal.Drg, error)
	GetDrg(id string, opts ...baremetal.Options) (*baremetal.Drg, error)
	DeleteDrg(id string, opts ...baremetal.Options) error
	ListDrgs(compartmentID string, opts ...baremetal.Options) (*baremetal.ListDrgs, error)

	CreateDrgAttachment(compartmentID, drgID, vcnID string, opts ...baremetal.Options) (vol *baremetal.DrgAttachment, e error)
	GetDrgAttachment(id string, opts ...baremetal.Options) (vol *baremetal.DrgAttachment, e error)
	DeleteDrgAttachment(id string, opts ...baremetal.Options) (e error)
	ListDrgAttachments(compartmentID string, opts ...baremetal.Options) (res *baremetal.ListDrgAttachments, e error)

	CreateInternetGateway(compartmentID, vcnID string, isEnabled bool, opts ...baremetal.Options) (gw *baremetal.InternetGateway, e error)
	GetInternetGateway(id string) (gw *baremetal.InternetGateway, e error)
	UpdateInternetGateway(id string, isEnabled bool, opts ...baremetal.Options) (gw *baremetal.InternetGateway, e error)
	DeleteInternetGateway(id string, opts ...baremetal.Options) (e error)
	ListInternetGateways(compartmentID, vcnID string, opts ...baremetal.Options) (l *baremetal.ListInternetGateways, e error)

	CreateRouteTable(compartmentID, vcnID string, routeRules []baremetal.RouteRule, opts ...baremetal.Options) (res *baremetal.RouteTable, e error)
	GetRouteTable(id string, opts ...baremetal.Options) (vol *baremetal.RouteTable, e error)
	UpdateRouteTable(id string, routeRules []baremetal.RouteRule, opts ...baremetal.Options) (res *baremetal.RouteTable, e error)
	DeleteRouteTable(id string, opts ...baremetal.Options) (e error)
	ListRouteTables(compartmentID, vcnID string, opts ...baremetal.Options) (vols *baremetal.ListRouteTables, e error)

	CaptureConsoleHistory(instanceID string, opts ...baremetal.Options) (icHistory *baremetal.ConsoleHistoryMetadata, e error)
	GetConsoleHistory(id string, opts ...baremetal.Options) (icHistory *baremetal.ConsoleHistoryMetadata, e error)
	DeleteConsoleHistory(id string) (e error)

	CreateOrResetUIPassword(userID string, opts ...baremetal.Options) (resource *baremetal.UIPassword, e error)

	CreateVolumeBackup(volumeID string, opts ...baremetal.Options) (vol *baremetal.VolumeBackup, e error)
	GetVolumeBackup(id string, opts ...baremetal.Options) (vol *baremetal.VolumeBackup, e error)
	UpdateVolumeBackup(id string, opts ...baremetal.Options) (vol *baremetal.VolumeBackup, e error)
	DeleteVolumeBackup(id string, opts ...baremetal.Options) (e error)

	GetVnic(vnicID string) (vnic *baremetal.Vnic, e error)
}
<|endoftext|>


<commit_before>package conn

import (
	"github.com/garyburd/redigo/redis"
	"log"
)

type Memo struct {
	Time    string `redis:"time"`
	Content string `redis:"content"`
}

type Task struct {
	Id     int
	ChatId int    `redis:"chatId"`
	Owner  string `redis:"owner"`
	Desc   string `redis:"content"`
	When   string `redis:"time"`
}

//All redis actions

func SetMasterId(id int) {
	c := Pool.Get()
	defer c.Close()
	c.Do("SET", "evolsnowChatId", id)
}

func GetMasterId() int {
	c := Pool.Get()
	defer c.Close()
	id, _ := redis.Int(c.Do("GET", "evolsnowChatId"))
	return id
}

func SetUserChatId(user string, id int) {
	c := Pool.Get()
	defer c.Close()
	key := user + "ChatId"
	c.Do("SET", key, id)
}

func GetUserChatId(user string) int {
	c := Pool.Get()
	defer c.Close()
	key := user + "ChatId"
	id, _ := redis.Int(c.Do("GET", key))
	return id
}

func HSetMemo(user, time, memo string) {
	c := Pool.Get()
	defer c.Close()
	var setMemoLua = `
	local id = redis.call("INCR", "memoIncrId")
	redis.call("RPUSH", KEYS[1]..":memos", id)
	redis.call("HMSET", "memo:"..id, "time", KEYS[2], "content", KEYS[3])
	`
	script := redis.NewScript(3, setMemoLua)
	script.Do(c, user, time, memo)
}

func GetTaskId() int {
	c := Pool.Get()
	defer c.Close()
	id, _ := redis.Int(c.Do("INCR", "taskIncrId"))
	return id
}

func HSetTask(ts Task) {
	c := Pool.Get()
	defer c.Close()
	var setTaskLua = `
	redis.call("RPUSH", KEYS[1]..":tasks", id)
	redis.call("HMSET", "task:"..id, "owner", KEYS[1], "time", KEYS[2], "content", KEYS[3], "chatID", KEYS[4])
	`
	script := redis.NewScript(4, setTaskLua)
	script.Do(c, ts.Owner, ts.When, ts.Desc, ts.ChatId)
}

func RemoveTask(ts Task) {
	c := Pool.Get()
	defer c.Close()
	log.Println("remove", ts.Id)
	var removeTaskLua = `
	redis.call("LREM", KEYS[1]..":tasks", 1, KEYS[2])
	redis.call("DEL", "task:"..KEYS[2])
	`
	script := redis.NewScript(2, removeTaskLua)
	script.Do(c, ts.Owner, ts.Id)
}

func HGetUserTasks(user string) []Task {
	c := Pool.Get()
	defer c.Close()
	var multiGetTaskLua = `
	local data = redis.call("LRANGE", KEYS[1]..":tasks", "0", "-1")
	local ret = {}
  	for idx=1, #data do
  		ret[idx] = redis.call("HGETALL", "task:"..data[idx])
  		print(ret[idx])
  	end
  	return ret
   `
	var tasks []Task
	script := redis.NewScript(1, multiGetTaskLua)
	values, err := redis.Values(script.Do(c, user))
	if err != nil {
		log.Println(err)
	}
	for i := range values {
		t := new(Task)
		redis.ScanStruct(values[i].([]interface{}), t)
		log.Println("task:", *t)
		tasks = append(tasks, *t)
	}
	return tasks
}

func HGetAllMemos(user string) []Memo {
	c := Pool.Get()
	defer c.Close()
	var multiGetMemoLua = `
	local data = redis.call("LRANGE", KEYS[1]..":memos", "0", "-1")
	local ret = {}
  	for idx=1, #data do
  		ret[idx] = redis.call("HGETALL", "memo:"..data[idx])
  	end
  	return ret
   `
	var memos []Memo
	script := redis.NewScript(1, multiGetMemoLua)
	values, err := redis.Values(script.Do(c, user))
	if err != nil {
		log.Println(err)
	}
	for i := range values {
		m := new(Memo)
		redis.ScanStruct(values[i].([]interface{}), m)
		memos = append(memos, *m)
	}
	return memos
}

//
//var multiGetScript = redis.NewScript(0, multiGetMemoLua)
<commit_msg>get tasks<commit_after>package conn

import (
	"github.com/garyburd/redigo/redis"
	"log"
)

type Memo struct {
	Time    string `redis:"time"`
	Content string `redis:"content"`
}

type Task struct {
	Id     int
	ChatId int    `redis:"chatId"`
	Owner  string `redis:"owner"`
	Desc   string `redis:"content"`
	When   string `redis:"time"`
}

//All redis actions

func SetMasterId(id int) {
	c := Pool.Get()
	defer c.Close()
	c.Do("SET", "evolsnowChatId", id)
}

func GetMasterId() int {
	c := Pool.Get()
	defer c.Close()
	id, _ := redis.Int(c.Do("GET", "evolsnowChatId"))
	return id
}

func SetUserChatId(user string, id int) {
	c := Pool.Get()
	defer c.Close()
	key := user + "ChatId"
	c.Do("SET", key, id)
}

func GetUserChatId(user string) int {
	c := Pool.Get()
	defer c.Close()
	key := user + "ChatId"
	id, _ := redis.Int(c.Do("GET", key))
	return id
}

func HSetMemo(user, time, memo string) {
	c := Pool.Get()
	defer c.Close()
	var setMemoLua = `
	local id = redis.call("INCR", "memoIncrId")
	redis.call("RPUSH", KEYS[1]..":memos", id)
	redis.call("HMSET", "memo:"..id, "time", KEYS[2], "content", KEYS[3])
	`
	script := redis.NewScript(3, setMemoLua)
	script.Do(c, user, time, memo)
}

func GetTaskId() int {
	c := Pool.Get()
	defer c.Close()
	id, _ := redis.Int(c.Do("INCR", "taskIncrId"))
	return id
}

func HSetTask(ts Task) {
	c := Pool.Get()
	defer c.Close()
	var setTaskLua = `
	redis.call("RPUSH", KEYS[2]..":tasks", id)
	redis.call("HMSET", "task:"..KEYS[1], "owner", KEYS[2], "time", KEYS[3], "content", KEYS[4], "chatID", KEYS[5])
	`
	script := redis.NewScript(5, setTaskLua)
	script.Do(c, ts.Id, ts.Owner, ts.When, ts.Desc, ts.ChatId)
}

func RemoveTask(ts Task) {
	c := Pool.Get()
	defer c.Close()
	log.Println("remove", ts.Id)
	var removeTaskLua = `
	redis.call("LREM", KEYS[1]..":tasks", 1, KEYS[2])
	redis.call("DEL", "task:"..KEYS[2])
	`
	script := redis.NewScript(2, removeTaskLua)
	script.Do(c, ts.Owner, ts.Id)
}

func HGetUserTasks(user string) []Task {
	c := Pool.Get()
	defer c.Close()
	var multiGetTaskLua = `
	local data = redis.call("LRANGE", KEYS[1]..":tasks", "0", "-1")
	local ret = {}
  	for idx=1, #data do
  		ret[idx] = redis.call("HGETALL", "task:"..data[idx])
  		print(ret[idx])
  	end
  	return ret
   `
	var tasks []Task
	script := redis.NewScript(1, multiGetTaskLua)
	values, err := redis.Values(script.Do(c, user))
	if err != nil {
		log.Println(err)
	}
	for i := range values {
		t := new(Task)
		redis.ScanStruct(values[i].([]interface{}), t)
		log.Println("task:", *t)
		tasks = append(tasks, *t)
	}
	return tasks
}

func HGetAllMemos(user string) []Memo {
	c := Pool.Get()
	defer c.Close()
	var multiGetMemoLua = `
	local data = redis.call("LRANGE", KEYS[1]..":memos", "0", "-1")
	local ret = {}
  	for idx=1, #data do
  		ret[idx] = redis.call("HGETALL", "memo:"..data[idx])
  	end
  	return ret
   `
	var memos []Memo
	script := redis.NewScript(1, multiGetMemoLua)
	values, err := redis.Values(script.Do(c, user))
	if err != nil {
		log.Println(err)
	}
	for i := range values {
		m := new(Memo)
		redis.ScanStruct(values[i].([]interface{}), m)
		memos = append(memos, *m)
	}
	return memos
}

//
//var multiGetScript = redis.NewScript(0, multiGetMemoLua)
<|endoftext|>


<commit_before>// licence goes here

package serial

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"os"
	"runtime"
)

// const

// Baud is the unit for the symbol rate. It describes the number of symbols transmitted per second.
type Baud uint32

const (
	// Baud4800 defines a transmission rate of 4800 symbols per second.
	Baud4800 = 4800
	// Baud9600 defines a transmission rate of 9600 symbols per second.
	Baud9600 = 9600
	// Baud19200 defines a transmission rate of 19200 symbols per second.
	Baud19200 = 19200
	// Baud38400 defines a transmission rate of 38400 symbols per second.
	Baud38400 = 38400
	// Baud57600 defines a transmission rate of 57600 symbols per second.
	Baud57600 = 57600
	// Baud115200 defines a transmission rate of 115200 symbols per second.
	Baud115200 = 115200
)

// DataBit is the number of bits representing a character.
type DataBit byte

const (
	// DataBit5 stands for a character length of five bits.
	DataBit5 = DataBit(iota + 5)
	// DataBit6 stands for a character length of six bits.
	DataBit6
	// DataBit7 stands for a character length of seven bits.
	DataBit7
	// DataBit8 stands for a character length of eight bits.
	DataBit8
)

// StopBit is the number of bits being send at the end of every character.
type StopBit byte

const (
	// StopBit1 represents a single bit being send as stopbit.
	StopBit1 = StopBit(iota + 1)
	// StopBit2 represents two bits being send as stopbit.
	StopBit2
)

// Parity is the method for detecting transmission errors.
type Parity byte

const (
	// ParityNone indicates that no error detection is being used.
	ParityNone = Parity(iota)
	// ParityEven indicates that a bit is added to even out the bit count.
	ParityEven
	// ParityOdd indicates that a bit is added to provide an odd bit count.
	ParityOdd
)

// TODO flow control

// var
var (
	errPort    = errors.New("serial configuration error: invalid port")
	errBaud    = errors.New("serial configuration error: invalid baud rate (4800, 9600, 19200, 38400, 57600, 115200)")
	errDataBit = errors.New("serial configuration error: invalid number of data bits (5, 6, 7, 8, 9)")
	errStopBit = errors.New("serial configuration error: invalid number of stop bits (1, 2)")
	errParity  = errors.New("serial configuration error: invalid parity (0 - None, 1 - Even, 2 - Odd)")
)

// structs and its functions

// Connection represents a serial connection with all parameters.
type Connection struct {
	Port    string
	Baud    Baud
	DataBit DataBit
	StopBit StopBit
	Parity  Parity
	f       *os.File
	open    bool
}

func (connection *Connection) check() error {

	switch runtime.GOOS {
	case "windows":
		//TODO Port should look like this: COM3, USB0
	case "freebsd", "linux":
		//TODO Port should look like this: /dev/ttyUSB0
	}

	switch connection.Baud {
	case Baud115200, Baud57600, Baud38400, Baud19200, Baud9600, Baud4800:
	default:
		return errBaud
	}

	switch connection.DataBit {
	case DataBit5, DataBit6, DataBit7, DataBit8:
	default:
		return errDataBit
	}

	switch connection.StopBit {
	case StopBit1, StopBit2:
	default:
		return errStopBit
	}

	switch connection.Parity {
	case ParityNone, ParityEven, ParityOdd:
	default:
		return errParity
	}

	return nil
}

func (connection *Connection) String() string {

	var parity string
	switch connection.Parity {
	case ParityNone:
		parity = "N"
	case ParityEven:
		parity = "E"
	case ParityOdd:
		parity = "O"
	}

	return fmt.Sprintf("port: %s, baud rate:%d, parameters: %d%s%d",
		connection.Port, connection.Baud, connection.DataBit, parity, connection.StopBit)
}

// functions

// Init provides a connection with the given parameters.
func Init(port string, baudrate Baud, databit DataBit, stopbit StopBit, parity Parity) (*Connection, error) {
	connection := &Connection{Port: port, Baud: baudrate, DataBit: databit, StopBit: stopbit, Parity: parity}
	return connection, connection.check()
}

// Load provides a connection with the parameters being loaded from a json file.
func Load(path string) (*Connection, error) {
	var connection *Connection

	file, err := os.Open(path)
	if err != nil {
		return connection, err
	}

	dec := json.NewDecoder(file)
	for {

		if err := dec.Decode(&connection); err == io.EOF {
			break
		} else if err != nil {
			return connection, err
		}
	}

	return connection, connection.check()
}
<commit_msg>updated comments<commit_after>// licence goes here

package serial

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"os"
)

// const

// Baud is the unit for the symbol rate. It describes the number of symbols transmitted per second.
type Baud uint32

const (
	// Baud4800 defines a transmission rate of 4800 symbols per second.
	Baud4800 = 4800
	// Baud9600 defines a transmission rate of 9600 symbols per second.
	Baud9600 = 9600
	// Baud19200 defines a transmission rate of 19200 symbols per second.
	Baud19200 = 19200
	// Baud38400 defines a transmission rate of 38400 symbols per second.
	Baud38400 = 38400
	// Baud57600 defines a transmission rate of 57600 symbols per second.
	Baud57600 = 57600
	// Baud115200 defines a transmission rate of 115200 symbols per second.
	Baud115200 = 115200
)

// DataBit is the number of bits representing a character.
type DataBit byte

const (
	// DataBit5 stands for a character length of five bits.
	DataBit5 = DataBit(iota + 5)
	// DataBit6 stands for a character length of six bits.
	DataBit6
	// DataBit7 stands for a character length of seven bits.
	DataBit7
	// DataBit8 stands for a character length of eight bits.
	DataBit8
)

// StopBit is the number of bits being send at the end of every character.
type StopBit byte

const (
	// StopBit1 represents a single bit being send as stopbit.
	StopBit1 = StopBit(iota + 1)
	// StopBit2 represents two bits being send as stopbit.
	StopBit2
)

// Parity is the method for detecting transmission errors.
type Parity byte

const (
	// ParityNone indicates that no error detection is being used.
	ParityNone = Parity(iota)
	// ParityEven indicates that a bit is added to even out the bit count.
	ParityEven
	// ParityOdd indicates that a bit is added to provide an odd bit count.
	ParityOdd
)

// TODO flow control

// var
var (
	errPort    = errors.New("serial configuration error: invalid port")
	errBaud    = errors.New("serial configuration error: invalid baud rate (4800, 9600, 19200, 38400, 57600, 115200)")
	errDataBit = errors.New("serial configuration error: invalid number of data bits (5, 6, 7, 8, 9)")
	errStopBit = errors.New("serial configuration error: invalid number of stop bits (1, 2)")
	errParity  = errors.New("serial configuration error: invalid parity (0 - None, 1 - Even, 2 - Odd)")
)

// structs and its functions

// Connection represents a serial connection with all parameters.
type Connection struct {
	Port    string
	Baud    Baud
	DataBit DataBit
	StopBit StopBit
	Parity  Parity
	f       *os.File
	open    bool
}

func (connection *Connection) check() error {

	switch connection.Baud {
	case Baud115200, Baud57600, Baud38400, Baud19200, Baud9600, Baud4800:
	default:
		return errBaud
	}

	switch connection.DataBit {
	case DataBit5, DataBit6, DataBit7, DataBit8:
	default:
		return errDataBit
	}

	switch connection.StopBit {
	case StopBit1, StopBit2:
	default:
		return errStopBit
	}

	switch connection.Parity {
	case ParityNone, ParityEven, ParityOdd:
	default:
		return errParity
	}

	return nil
}

func (connection *Connection) String() string {

	var parity string
	switch connection.Parity {
	case ParityNone:
		parity = "N"
	case ParityEven:
		parity = "E"
	case ParityOdd:
		parity = "O"
	}

	return fmt.Sprintf("port: %s, baud rate:%d, parameters: %d%s%d",
		connection.Port, connection.Baud, connection.DataBit, parity, connection.StopBit)
}

// functions

// Init provides a connection with the given parameters.
func Init(port string, baudrate Baud, databit DataBit, stopbit StopBit, parity Parity) (*Connection, error) {
	connection := &Connection{Port: port, Baud: baudrate, DataBit: databit, StopBit: stopbit, Parity: parity}
	return connection, connection.check()
}

// Load provides a connection with the parameters being loaded from a json file.
func Load(path string) (*Connection, error) {
	var connection *Connection

	file, err := os.Open(path)
	if err != nil {
		return connection, err
	}

	dec := json.NewDecoder(file)
	for {

		if err := dec.Decode(&connection); err == io.EOF {
			break
		} else if err != nil {
			return connection, err
		}
	}

	return connection, connection.check()
}
<|endoftext|>


<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 fuse

import (
	"fmt"
	"log"
	"path"
	"runtime"
	"sync"

	"golang.org/x/net/context"

	"github.com/jacobsa/bazilfuse"
	"github.com/jacobsa/fuse/fuseops"
)

// A connection to the fuse kernel process.
type Connection struct {
	logger      *log.Logger
	wrapped     *bazilfuse.Conn
	opsInFlight sync.WaitGroup

	// The context from which all op contexts inherit.
	parentCtx context.Context

	// For logging purposes only.
	nextOpID uint32

	mu sync.Mutex

	// A map from bazilfuse request ID (*not* the op ID for logging used above)
	// to a function that cancel's its associated context.
	//
	// GUARDED_BY(mu)
	cancelFuncs map[bazilfuse.RequestID]func()
}

// Responsibility for closing the wrapped connection is transferred to the
// result. You must call c.close() eventually.
func newConnection(
	parentCtx context.Context,
	logger *log.Logger,
	wrapped *bazilfuse.Conn) (c *Connection, err error) {
	c = &Connection{
		logger:    logger,
		wrapped:   wrapped,
		parentCtx: parentCtx,
	}

	return
}

// Log information for an operation with the given ID. calldepth is the depth
// to use when recovering file:line information with runtime.Caller.
func (c *Connection) log(
	opID uint32,
	calldepth int,
	format string,
	v ...interface{}) {
	// Get file:line info.
	var file string
	var line int
	var ok bool

	_, file, line, ok = runtime.Caller(calldepth)
	if !ok {
		file = "???"
	}

	fileLine := fmt.Sprintf("%v:%v", path.Base(file), line)

	// Format the actual message to be printed.
	msg := fmt.Sprintf(
		"Op 0x%08x %24s] %v",
		opID,
		fileLine,
		fmt.Sprintf(format, v...))

	// Print it.
	c.logger.Println(msg)
}

// LOCKS_EXCLUDED(c.mu)
func (c *Connection) recordCancelFunc(
	reqID bazilfuse.RequestID,
	f func()) {
	c.mu.Lock()
	defer c.mu.Unlock()

	if _, ok := c.cancelFuncs[reqID]; ok {
		panic(fmt.Sprintf("Already have cancel func for request %v", reqID))
	}

	c.cancelFuncs[reqID] = f
}

// Set up state for an op that is about to be returned to the user, given its
// bazilfuse request ID.
//
// Return a context that should be used for the op.
//
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) beginOp(reqID bazilfuse.RequestID) (ctx context.Context) {
	// Note that the op is in flight.
	c.opsInFlight.Add(1)

	// Set up a cancellation function.
	ctx, cancel := context.WithCancel(c.parentCtx)
	c.recordCancelFunc(reqID, cancel)

	return
}

// Clean up all state associated with an op to which the user has responded.
//
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) finishOp(reqID bazilfuse.RequestID) {
	c.mu.Lock()
	defer c.mu.Unlock()

	// Even though the op is finished, context.WithCancel requires us to arrange
	// for the cancellation function to be invoked. We also must remove it from
	// our map.
	cancel, ok := c.cancelFuncs[reqID]
	if !ok {
		panic(fmt.Sprintf("Unknown request ID in finishOp: %v", reqID))
	}

	cancel()
	delete(c.cancelFuncs, reqID)

	// Decrement the in-flight counter.
	c.opsInFlight.Done()
}

// Read the next op from the kernel process. Return io.EOF if the kernel has
// closed the connection.
//
// This function delivers ops in exactly the order they are received from
// /dev/fuse. It must not be called multiple times concurrently.
//
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) ReadOp() (op fuseops.Op, err error) {
	// Keep going until we find a request we know how to convert.
	for {
		// Read a bazilfuse request.
		var bfReq bazilfuse.Request
		bfReq, err = c.wrapped.ReadRequest()

		if err != nil {
			return
		}

		// Choose an ID for this operation.
		opID := c.nextOpID
		c.nextOpID++

		// Log the receipt of the operation.
		c.log(opID, 1, "<- %v", bfReq)

		// Special case: responding to this is required to make mounting work on OS
		// X. We don't currently expose the capability for the file system to
		// intercept this.
		if statfsReq, ok := bfReq.(*bazilfuse.StatfsRequest); ok {
			c.log(opID, 1, "-> (Statfs) OK")
			statfsReq.Respond(&bazilfuse.StatfsResponse{})
			continue
		}

		// Set up op dependencies.
		var reqID bazilfuse.RequestID = bfReq.Hdr().ID
		opCtx := c.beginOp(reqID)

		logForOp := func(calldepth int, format string, v ...interface{}) {
			c.log(opID, calldepth+1, format, v...)
		}

		finished := func(err error) { c.finishOp(reqID) }

		op = fuseops.Convert(opCtx, bfReq, logForOp, finished)
		return
	}
}

func (c *Connection) waitForReady() (err error) {
	<-c.wrapped.Ready
	err = c.wrapped.MountError
	return
}

// Close the connection and wait for in-flight ops.
func (c *Connection) close() (err error) {
	err = c.wrapped.Close()
	c.opsInFlight.Wait()
	return
}
<commit_msg>Fixed a panic.<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 fuse

import (
	"fmt"
	"log"
	"path"
	"runtime"
	"sync"

	"golang.org/x/net/context"

	"github.com/jacobsa/bazilfuse"
	"github.com/jacobsa/fuse/fuseops"
)

// A connection to the fuse kernel process.
type Connection struct {
	logger      *log.Logger
	wrapped     *bazilfuse.Conn
	opsInFlight sync.WaitGroup

	// The context from which all op contexts inherit.
	parentCtx context.Context

	// For logging purposes only.
	nextOpID uint32

	mu sync.Mutex

	// A map from bazilfuse request ID (*not* the op ID for logging used above)
	// to a function that cancel's its associated context.
	//
	// GUARDED_BY(mu)
	cancelFuncs map[bazilfuse.RequestID]func()
}

// Responsibility for closing the wrapped connection is transferred to the
// result. You must call c.close() eventually.
func newConnection(
	parentCtx context.Context,
	logger *log.Logger,
	wrapped *bazilfuse.Conn) (c *Connection, err error) {
	c = &Connection{
		logger:      logger,
		wrapped:     wrapped,
		parentCtx:   parentCtx,
		cancelFuncs: make(map[bazilfuse.RequestID]func()),
	}

	return
}

// Log information for an operation with the given ID. calldepth is the depth
// to use when recovering file:line information with runtime.Caller.
func (c *Connection) log(
	opID uint32,
	calldepth int,
	format string,
	v ...interface{}) {
	// Get file:line info.
	var file string
	var line int
	var ok bool

	_, file, line, ok = runtime.Caller(calldepth)
	if !ok {
		file = "???"
	}

	fileLine := fmt.Sprintf("%v:%v", path.Base(file), line)

	// Format the actual message to be printed.
	msg := fmt.Sprintf(
		"Op 0x%08x %24s] %v",
		opID,
		fileLine,
		fmt.Sprintf(format, v...))

	// Print it.
	c.logger.Println(msg)
}

// LOCKS_EXCLUDED(c.mu)
func (c *Connection) recordCancelFunc(
	reqID bazilfuse.RequestID,
	f func()) {
	c.mu.Lock()
	defer c.mu.Unlock()

	if _, ok := c.cancelFuncs[reqID]; ok {
		panic(fmt.Sprintf("Already have cancel func for request %v", reqID))
	}

	c.cancelFuncs[reqID] = f
}

// Set up state for an op that is about to be returned to the user, given its
// bazilfuse request ID.
//
// Return a context that should be used for the op.
//
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) beginOp(reqID bazilfuse.RequestID) (ctx context.Context) {
	// Note that the op is in flight.
	c.opsInFlight.Add(1)

	// Set up a cancellation function.
	ctx, cancel := context.WithCancel(c.parentCtx)
	c.recordCancelFunc(reqID, cancel)

	return
}

// Clean up all state associated with an op to which the user has responded.
//
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) finishOp(reqID bazilfuse.RequestID) {
	c.mu.Lock()
	defer c.mu.Unlock()

	// Even though the op is finished, context.WithCancel requires us to arrange
	// for the cancellation function to be invoked. We also must remove it from
	// our map.
	cancel, ok := c.cancelFuncs[reqID]
	if !ok {
		panic(fmt.Sprintf("Unknown request ID in finishOp: %v", reqID))
	}

	cancel()
	delete(c.cancelFuncs, reqID)

	// Decrement the in-flight counter.
	c.opsInFlight.Done()
}

// Read the next op from the kernel process. Return io.EOF if the kernel has
// closed the connection.
//
// This function delivers ops in exactly the order they are received from
// /dev/fuse. It must not be called multiple times concurrently.
//
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) ReadOp() (op fuseops.Op, err error) {
	// Keep going until we find a request we know how to convert.
	for {
		// Read a bazilfuse request.
		var bfReq bazilfuse.Request
		bfReq, err = c.wrapped.ReadRequest()

		if err != nil {
			return
		}

		// Choose an ID for this operation.
		opID := c.nextOpID
		c.nextOpID++

		// Log the receipt of the operation.
		c.log(opID, 1, "<- %v", bfReq)

		// Special case: responding to this is required to make mounting work on OS
		// X. We don't currently expose the capability for the file system to
		// intercept this.
		if statfsReq, ok := bfReq.(*bazilfuse.StatfsRequest); ok {
			c.log(opID, 1, "-> (Statfs) OK")
			statfsReq.Respond(&bazilfuse.StatfsResponse{})
			continue
		}

		// Set up op dependencies.
		var reqID bazilfuse.RequestID = bfReq.Hdr().ID
		opCtx := c.beginOp(reqID)

		logForOp := func(calldepth int, format string, v ...interface{}) {
			c.log(opID, calldepth+1, format, v...)
		}

		finished := func(err error) { c.finishOp(reqID) }

		op = fuseops.Convert(opCtx, bfReq, logForOp, finished)
		return
	}
}

func (c *Connection) waitForReady() (err error) {
	<-c.wrapped.Ready
	err = c.wrapped.MountError
	return
}

// Close the connection and wait for in-flight ops.
func (c *Connection) close() (err error) {
	err = c.wrapped.Close()
	c.opsInFlight.Wait()
	return
}
<|endoftext|>


<commit_before>package sphere

import (
	"net/http"
	"time"

	"github.com/gorilla/websocket"
)

// NewConnection returns a new ws connection instance
func NewConnection(w http.ResponseWriter, r *http.Request) (*Connection, error) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err == nil {
		return &Connection{guid.String(), []*Channel{}, make(chan []byte), r, conn}, nil
	}
	return nil, err
}

// Connection allows you to interact with backend and other client sockets in realtime
type Connection struct {
	// the id of the connection
	id string
	// list of channels that this connection has been subscribed
	channels []*Channel
	// buffered channel of outbound messages
	send chan []byte
	// http request
	request *http.Request
	// websocket connection
	*websocket.Conn
}

// write writes a message with the given message type and payload.
func (conn *Connection) emit(mt int, payload []byte) error {
	conn.SetWriteDeadline(time.Now().Add(writeWait))
	return conn.WriteMessage(mt, payload)
}

// writePump pumps messages from the sphere to the websocket connection.
func (conn *Connection) writePump() error {
	ticker := time.NewTicker(pingPeriod)
	defer func() {
		ticker.Stop()
	}()
	for {
		select {
		case msg, ok := <-conn.send:
			if !ok {
				return conn.emit(websocket.CloseMessage, []byte{})
			}
			if err := conn.emit(websocket.TextMessage, msg); err != nil {
				return err
			}
		case <-ticker.C:
			if err := conn.emit(websocket.PingMessage, []byte{}); err != nil {
				return err
			}
		}
	}
}

func (conn *Connection) readPump() {
	conn.SetReadLimit(maxMessageSize)
	conn.SetReadDeadline(time.Now().Add(pongWait))
	conn.SetPingHandler(func(string) error { conn.SetReadDeadline(time.Now().Add(pongWait)); return nil })
	for {
		_, msg, err := conn.ReadMessage()
		if err != nil {
		}
		if msg != nil {
		}
	}
}
<commit_msg>connection improvments<commit_after>package sphere

import (
	"net/http"
	"time"

	"github.com/gorilla/websocket"
)

// NewConnection returns a new ws connection instance
func NewConnection(w http.ResponseWriter, r *http.Request) (*Connection, error) {
	ws, err := upgrader.Upgrade(w, r, nil)
	if err == nil {
		conn := &Connection{guid.String(), []*Channel{}, make(chan []byte), make(chan *Packet), r, ws}
		go conn.queue()
		return conn, nil
	}
	return nil, err
}

// Connection allows you to interact with backend and other client sockets in realtime
type Connection struct {
	// the id of the connection
	id string
	// list of channels that this connection has been subscribed
	channels []*Channel
	// buffered channel of outbound messages
	send chan []byte
	// buffered channel of inbound messages
	receive chan *Packet
	// http request
	request *http.Request
	// websocket connection
	*websocket.Conn
}

func (conn *Connection) queue() {
	for {
		select {
		case <-conn.receive:
		}
	}
}

// write writes a message with the given message type and payload.
func (conn *Connection) emit(mt int, payload []byte) error {
	conn.SetWriteDeadline(time.Now().Add(writeWait))
	return conn.WriteMessage(mt, payload)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
	"syscall"

	"github.com/dickeyxxx/golock"
	"github.com/heroku/heroku-cli/gode"
)

// Plugin represents a javascript plugin
type Plugin struct {
	Name     string     `json:"name"`
	Version  string     `json:"version"`
	Topics   TopicSet   `json:"topics"`
	Topic    *Topic     `json:"topic"`
	Commands CommandSet `json:"commands"`
}

// SetupNode sets up node and npm in ~/.heroku
func SetupNode() {
	gode.SetRootPath(AppDir())
	setup, err := gode.IsSetup()
	PrintError(err, false)
	if !setup {
		PrintError(gode.Setup(), true)
	}
}

// LoadPlugins loads the topics and commands from the JavaScript plugins into the CLI
func (cli *Cli) LoadPlugins(plugins map[string]*Plugin) {
	for _, plugin := range plugins {
		for _, topic := range plugin.Topics {
			cli.AddTopic(topic)
		}
		if plugin.Topic != nil {
			cli.AddTopic(plugin.Topic)
		}
		for _, command := range plugin.Commands {
			if !cli.AddCommand(command) {
				Errf("WARNING: command %s has already been defined\n", command)
			}
		}
	}
	sort.Sort(cli.Topics)
	sort.Sort(cli.Commands)
}

var pluginsTopic = &Topic{
	Name:        "plugins",
	Description: "manage plugins",
}

var pluginsInstallCmd = &Command{
	Topic:       "plugins",
	Command:     "install",
	Hidden:      true,
	Args:        []Arg{{Name: "name"}},
	Description: "Installs a plugin into the CLI",
	Help: `Install a Heroku plugin

  Example:
  $ heroku plugins:install dickeyxxx/heroku-production-status`,

	Run: func(ctx *Context) {
		name := ctx.Args.(map[string]string)["name"]
		if len(name) == 0 {
			Errln("Must specify a plugin name")
			return
		}
		Errf("Installing plugin %s...", name)
		ExitIfError(installPlugins(name), true)
		Errln(" done")
	},
}

var pluginsLinkCmd = &Command{
	Topic:       "plugins",
	Command:     "link",
	Description: "Links a local plugin into CLI",
	Args:        []Arg{{Name: "path", Optional: true}},
	Help: `Links a local plugin into CLI.
	This is useful when developing plugins locally.
	It simply symlinks the specified path into ~/.heroku/node_modules

  Example:
	$ heroku plugins:link .`,

	Run: func(ctx *Context) {
		path := ctx.Args.(map[string]string)["path"]
		if path == "" {
			path = "."
		}
		path, err := filepath.Abs(path)
		if err != nil {
			panic(err)
		}
		if _, err := os.Stat(path); err != nil {
			panic(err)
		}
		name := filepath.Base(path)
		newPath := pluginPath(name)
		os.Remove(newPath)
		os.RemoveAll(newPath)
		err = os.Symlink(path, newPath)
		if err != nil {
			panic(err)
		}
		plugin, err := ParsePlugin(name)
		ExitIfError(err, false)
		if name != plugin.Name {
			path = newPath
			newPath = pluginPath(plugin.Name)
			os.Remove(newPath)
			os.RemoveAll(newPath)
			os.Rename(path, newPath)
		}
		Println("Symlinked", plugin.Name)
		AddPluginsToCache(plugin)
	},
}

var pluginsUninstallCmd = &Command{
	Topic:       "plugins",
	Command:     "uninstall",
	Hidden:      true,
	Args:        []Arg{{Name: "name"}},
	Description: "Uninstalls a plugin from the CLI",
	Help: `Uninstalls a Heroku plugin

  Example:
  $ heroku plugins:uninstall heroku-production-status`,

	Run: func(ctx *Context) {
		name := ctx.Args.(map[string]string)["name"]
		if !contains(PluginNames(), name) {
			ExitIfError(errors.New(name+" is not installed"), false)
		}
		Errf("Uninstalling plugin %s...", name)
		ExitIfError(gode.RemovePackages(name), true)
		RemovePluginFromCache(name)
		Errln(" done")
	},
}

var pluginsListCmd = &Command{
	Topic:       "plugins",
	Hidden:      true,
	Description: "Lists installed plugins",
	Help: `
Example:
  $ heroku plugins`,

	Run: func(ctx *Context) {
		SetupBuiltinPlugins()
		var plugins []string
		for _, plugin := range GetPlugins() {
			if plugin != nil && len(plugin.Commands) > 0 {
				symlinked := ""
				if isPluginSymlinked(plugin.Name) {
					symlinked = " (symlinked)"
				}
				plugins = append(plugins, fmt.Sprintf("%s %s %s", plugin.Name, plugin.Version, symlinked))
			}
		}
		sort.Strings(plugins)
		for _, plugin := range plugins {
			Println(plugin)
		}
	},
}

func runFn(plugin *Plugin, topic, command string) func(ctx *Context) {
	return func(ctx *Context) {
		readLockPlugin(plugin.Name)
		ctx.Dev = isPluginSymlinked(plugin.Name)
		ctxJSON, err := json.Marshal(ctx)
		if err != nil {
			panic(err)
		}
		title, _ := json.Marshal(processTitle(ctx))
		script := fmt.Sprintf(`
		'use strict';
		var moduleName = '%s';
		var moduleVersion = '%s';
		var topic = '%s';
		var command = '%s';
		process.title = %s;
		var ctx = %s;
		ctx.version = ctx.version + ' ' + moduleName + '/' + moduleVersion + ' node-' + process.version;
		var logPath = %s;
		process.chdir(ctx.cwd);
		if (!ctx.dev) {
			process.on('uncaughtException', function (err) {
				// ignore EPIPE errors (usually from piping to head)
				if (err.code === "EPIPE") return;
				console.error(' !   Error in ' + moduleName + ':')
				console.error(' !   ' + err.message || err);
				if (err.stack) {
					var fs = require('fs');
					var log = function (line) {
						var d = new Date().toISOString()
						.replace(/T/, ' ')
						.replace(/-/g, '/')
						.replace(/\..+/, '');
						fs.appendFileSync(logPath, d + ' ' + line + '\n');
					}
					log('Error during ' + topic + ':' + command);
					log(err.stack);
					console.error(' !   See ' + logPath + ' for more info.');
				}
				process.exit(1);
			});
		}
		if (command === '') { command = null }
		var module = require(moduleName);
		var cmd = module.commands.filter(function (c) {
			return c.topic === topic && c.command == command;
		})[0];
		cmd.run(ctx);`, plugin.Name, plugin.Version, topic, command, string(title), ctxJSON, strconv.Quote(ErrLogPath))

		// swallow sigint since the plugin will handle it
		swallowSignal(os.Interrupt)

		cmd := gode.RunScript(script)
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		if ctx.Flags["debugger"] == true {
			cmd = gode.DebugScript(script)
		}
		if err := cmd.Run(); err != nil {
			os.Exit(getExitCode(err))
		}
	}
}

func swallowSignal(s os.Signal) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, s)
	go func() {
		<-c
	}()
}

func getExitCode(err error) int {
	switch e := err.(type) {
	case nil:
		return 0
	case *exec.ExitError:
		status, ok := e.Sys().(syscall.WaitStatus)
		if !ok {
			panic(err)
		}
		return status.ExitStatus()
	default:
		panic(err)
	}
}

// ParsePlugin requires the plugin's node module
// to get the commands and metadata
func ParsePlugin(name string) (*Plugin, error) {
	script := `
	var plugin = require('` + name + `');
	if (!plugin.commands) throw new Error('Contains no commands. Is this a real plugin?');
	var pjson  = require('` + name + `/package.json');

	plugin.name    = pjson.name;
	plugin.version = pjson.version;

	console.log(JSON.stringify(plugin))`
	cmd := gode.RunScript(script)
	cmd.Stderr = Stderr
	output, err := cmd.Output()
	if err != nil {
		return nil, fmt.Errorf("Error reading plugin: %s\n%s", name, err)
	}
	var plugin Plugin
	err = json.Unmarshal([]byte(output), &plugin)
	if err != nil {
		return nil, fmt.Errorf("Error parsing plugin: %s\n%s\n%s", name, err, string(output))
	}
	for _, command := range plugin.Commands {
		if command == nil {
			continue
		}
		command.Plugin = plugin.Name
		command.Help = strings.TrimSpace(command.Help)
	}
	return &plugin, nil
}

// GetPlugins goes through all the node plugins and returns them in Go stucts
func GetPlugins() map[string]*Plugin {
	plugins := FetchPluginCache()
	for name, plugin := range plugins {
		if plugin == nil || !pluginExists(name) {
			delete(plugins, name)
		} else {
			for _, command := range plugin.Commands {
				command.Run = runFn(plugin, command.Topic, command.Command)
			}
		}
	}
	return plugins
}

// PluginNames lists all the plugin names
func PluginNames() []string {
	plugins := FetchPluginCache()
	names := make([]string, 0, len(plugins))
	for _, plugin := range plugins {
		if plugin != nil && pluginExists(plugin.Name) && len(plugin.Commands) > 0 {
			names = append(names, plugin.Name)
		}
	}
	return names
}

// PluginNamesNotSymlinked returns all the plugins that are not symlinked
func PluginNamesNotSymlinked() []string {
	a := PluginNames()
	b := make([]string, 0, len(a))
	for _, plugin := range a {
		if !isPluginSymlinked(plugin) {
			b = append(b, plugin)
		}
	}
	return b
}

func isPluginSymlinked(plugin string) bool {
	path := filepath.Join(AppDir(), "node_modules", plugin)
	fi, err := os.Lstat(path)
	if err != nil {
		return false
	}
	return fi.Mode()&os.ModeSymlink != 0
}

// SetupBuiltinPlugins ensures all the builtinPlugins are installed
func SetupBuiltinPlugins() {
	pluginNames := difference(BuiltinPlugins, PluginNames())
	if len(pluginNames) == 0 {
		return
	}
	Err("heroku-cli: Installing core plugins...")
	if err := installPlugins(pluginNames...); err != nil {
		// retry once
		PrintError(gode.RemovePackages(pluginNames...), true)
		PrintError(gode.ClearCache(), true)
		Err("\rheroku-cli: Installing core plugins (retrying)...")
		ExitIfError(installPlugins(pluginNames...), true)
	}
	Errln(" done")
}

func difference(a, b []string) []string {
	res := make([]string, 0, len(a))
	for _, aa := range a {
		if !contains(b, aa) {
			res = append(res, aa)
		}
	}
	return res
}

func contains(arr []string, s string) bool {
	for _, a := range arr {
		if a == s {
			return true
		}
	}
	return false
}

func installPlugins(names ...string) error {
	for _, name := range names {
		lockPlugin(name)
	}
	defer func() {
		for _, name := range names {
			unlockPlugin(name)
		}
	}()
	err := gode.InstallPackages(names...)
	if err != nil {
		return err
	}
	plugins := make([]*Plugin, 0, len(names))
	for _, name := range names {
		plugin, err := ParsePlugin(name)
		if err != nil {
			return err
		}
		plugins = append(plugins, plugin)
	}
	AddPluginsToCache(plugins...)
	return nil
}

func pluginExists(plugin string) bool {
	exists, _ := fileExists(pluginPath(plugin))
	return exists
}

// directory location of plugin
func pluginPath(plugin string) string {
	return filepath.Join(AppDir(), "node_modules", plugin)
}

// lock a plugin for reading
func readLockPlugin(name string) {
	lockfile := updateLockPath + "." + name
	locked, err := golock.IsLocked(lockfile)
	LogIfError(err)
	if locked {
		lockPlugin(name)
		unlockPlugin(name)
	}
}

// lock a plugin for writing
func lockPlugin(name string) {
	LogIfError(golock.Lock(updateLockPath + "." + name))
}

// unlock a plugin
func unlockPlugin(name string) {
	LogIfError(golock.Unlock(updateLockPath + "." + name))
}
<commit_msg>panic if gode fails to setup<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"os/signal"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
	"syscall"

	"github.com/dickeyxxx/golock"
	"github.com/heroku/heroku-cli/gode"
)

// Plugin represents a javascript plugin
type Plugin struct {
	Name     string     `json:"name"`
	Version  string     `json:"version"`
	Topics   TopicSet   `json:"topics"`
	Topic    *Topic     `json:"topic"`
	Commands CommandSet `json:"commands"`
}

// SetupNode sets up node and npm in ~/.heroku
func SetupNode() {
	gode.SetRootPath(AppDir())
	setup, err := gode.IsSetup()
	PrintError(err, false)
	if !setup {
		panic(gode.Setup())
	}
}

// LoadPlugins loads the topics and commands from the JavaScript plugins into the CLI
func (cli *Cli) LoadPlugins(plugins map[string]*Plugin) {
	for _, plugin := range plugins {
		for _, topic := range plugin.Topics {
			cli.AddTopic(topic)
		}
		if plugin.Topic != nil {
			cli.AddTopic(plugin.Topic)
		}
		for _, command := range plugin.Commands {
			if !cli.AddCommand(command) {
				Errf("WARNING: command %s has already been defined\n", command)
			}
		}
	}
	sort.Sort(cli.Topics)
	sort.Sort(cli.Commands)
}

var pluginsTopic = &Topic{
	Name:        "plugins",
	Description: "manage plugins",
}

var pluginsInstallCmd = &Command{
	Topic:       "plugins",
	Command:     "install",
	Hidden:      true,
	Args:        []Arg{{Name: "name"}},
	Description: "Installs a plugin into the CLI",
	Help: `Install a Heroku plugin

  Example:
  $ heroku plugins:install dickeyxxx/heroku-production-status`,

	Run: func(ctx *Context) {
		name := ctx.Args.(map[string]string)["name"]
		if len(name) == 0 {
			Errln("Must specify a plugin name")
			return
		}
		Errf("Installing plugin %s...", name)
		ExitIfError(installPlugins(name), true)
		Errln(" done")
	},
}

var pluginsLinkCmd = &Command{
	Topic:       "plugins",
	Command:     "link",
	Description: "Links a local plugin into CLI",
	Args:        []Arg{{Name: "path", Optional: true}},
	Help: `Links a local plugin into CLI.
	This is useful when developing plugins locally.
	It simply symlinks the specified path into ~/.heroku/node_modules

  Example:
	$ heroku plugins:link .`,

	Run: func(ctx *Context) {
		path := ctx.Args.(map[string]string)["path"]
		if path == "" {
			path = "."
		}
		path, err := filepath.Abs(path)
		if err != nil {
			panic(err)
		}
		if _, err := os.Stat(path); err != nil {
			panic(err)
		}
		name := filepath.Base(path)
		newPath := pluginPath(name)
		os.Remove(newPath)
		os.RemoveAll(newPath)
		err = os.Symlink(path, newPath)
		if err != nil {
			panic(err)
		}
		plugin, err := ParsePlugin(name)
		ExitIfError(err, false)
		if name != plugin.Name {
			path = newPath
			newPath = pluginPath(plugin.Name)
			os.Remove(newPath)
			os.RemoveAll(newPath)
			os.Rename(path, newPath)
		}
		Println("Symlinked", plugin.Name)
		AddPluginsToCache(plugin)
	},
}

var pluginsUninstallCmd = &Command{
	Topic:       "plugins",
	Command:     "uninstall",
	Hidden:      true,
	Args:        []Arg{{Name: "name"}},
	Description: "Uninstalls a plugin from the CLI",
	Help: `Uninstalls a Heroku plugin

  Example:
  $ heroku plugins:uninstall heroku-production-status`,

	Run: func(ctx *Context) {
		name := ctx.Args.(map[string]string)["name"]
		if !contains(PluginNames(), name) {
			ExitIfError(errors.New(name+" is not installed"), false)
		}
		Errf("Uninstalling plugin %s...", name)
		ExitIfError(gode.RemovePackages(name), true)
		RemovePluginFromCache(name)
		Errln(" done")
	},
}

var pluginsListCmd = &Command{
	Topic:       "plugins",
	Hidden:      true,
	Description: "Lists installed plugins",
	Help: `
Example:
  $ heroku plugins`,

	Run: func(ctx *Context) {
		SetupBuiltinPlugins()
		var plugins []string
		for _, plugin := range GetPlugins() {
			if plugin != nil && len(plugin.Commands) > 0 {
				symlinked := ""
				if isPluginSymlinked(plugin.Name) {
					symlinked = " (symlinked)"
				}
				plugins = append(plugins, fmt.Sprintf("%s %s %s", plugin.Name, plugin.Version, symlinked))
			}
		}
		sort.Strings(plugins)
		for _, plugin := range plugins {
			Println(plugin)
		}
	},
}

func runFn(plugin *Plugin, topic, command string) func(ctx *Context) {
	return func(ctx *Context) {
		readLockPlugin(plugin.Name)
		ctx.Dev = isPluginSymlinked(plugin.Name)
		ctxJSON, err := json.Marshal(ctx)
		if err != nil {
			panic(err)
		}
		title, _ := json.Marshal(processTitle(ctx))
		script := fmt.Sprintf(`
		'use strict';
		var moduleName = '%s';
		var moduleVersion = '%s';
		var topic = '%s';
		var command = '%s';
		process.title = %s;
		var ctx = %s;
		ctx.version = ctx.version + ' ' + moduleName + '/' + moduleVersion + ' node-' + process.version;
		var logPath = %s;
		process.chdir(ctx.cwd);
		if (!ctx.dev) {
			process.on('uncaughtException', function (err) {
				// ignore EPIPE errors (usually from piping to head)
				if (err.code === "EPIPE") return;
				console.error(' !   Error in ' + moduleName + ':')
				console.error(' !   ' + err.message || err);
				if (err.stack) {
					var fs = require('fs');
					var log = function (line) {
						var d = new Date().toISOString()
						.replace(/T/, ' ')
						.replace(/-/g, '/')
						.replace(/\..+/, '');
						fs.appendFileSync(logPath, d + ' ' + line + '\n');
					}
					log('Error during ' + topic + ':' + command);
					log(err.stack);
					console.error(' !   See ' + logPath + ' for more info.');
				}
				process.exit(1);
			});
		}
		if (command === '') { command = null }
		var module = require(moduleName);
		var cmd = module.commands.filter(function (c) {
			return c.topic === topic && c.command == command;
		})[0];
		cmd.run(ctx);`, plugin.Name, plugin.Version, topic, command, string(title), ctxJSON, strconv.Quote(ErrLogPath))

		// swallow sigint since the plugin will handle it
		swallowSignal(os.Interrupt)

		cmd := gode.RunScript(script)
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		if ctx.Flags["debugger"] == true {
			cmd = gode.DebugScript(script)
		}
		if err := cmd.Run(); err != nil {
			os.Exit(getExitCode(err))
		}
	}
}

func swallowSignal(s os.Signal) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, s)
	go func() {
		<-c
	}()
}

func getExitCode(err error) int {
	switch e := err.(type) {
	case nil:
		return 0
	case *exec.ExitError:
		status, ok := e.Sys().(syscall.WaitStatus)
		if !ok {
			panic(err)
		}
		return status.ExitStatus()
	default:
		panic(err)
	}
}

// ParsePlugin requires the plugin's node module
// to get the commands and metadata
func ParsePlugin(name string) (*Plugin, error) {
	script := `
	var plugin = require('` + name + `');
	if (!plugin.commands) throw new Error('Contains no commands. Is this a real plugin?');
	var pjson  = require('` + name + `/package.json');

	plugin.name    = pjson.name;
	plugin.version = pjson.version;

	console.log(JSON.stringify(plugin))`
	cmd := gode.RunScript(script)
	cmd.Stderr = Stderr
	output, err := cmd.Output()
	if err != nil {
		return nil, fmt.Errorf("Error reading plugin: %s\n%s", name, err)
	}
	var plugin Plugin
	err = json.Unmarshal([]byte(output), &plugin)
	if err != nil {
		return nil, fmt.Errorf("Error parsing plugin: %s\n%s\n%s", name, err, string(output))
	}
	for _, command := range plugin.Commands {
		if command == nil {
			continue
		}
		command.Plugin = plugin.Name
		command.Help = strings.TrimSpace(command.Help)
	}
	return &plugin, nil
}

// GetPlugins goes through all the node plugins and returns them in Go stucts
func GetPlugins() map[string]*Plugin {
	plugins := FetchPluginCache()
	for name, plugin := range plugins {
		if plugin == nil || !pluginExists(name) {
			delete(plugins, name)
		} else {
			for _, command := range plugin.Commands {
				command.Run = runFn(plugin, command.Topic, command.Command)
			}
		}
	}
	return plugins
}

// PluginNames lists all the plugin names
func PluginNames() []string {
	plugins := FetchPluginCache()
	names := make([]string, 0, len(plugins))
	for _, plugin := range plugins {
		if plugin != nil && pluginExists(plugin.Name) && len(plugin.Commands) > 0 {
			names = append(names, plugin.Name)
		}
	}
	return names
}

// PluginNamesNotSymlinked returns all the plugins that are not symlinked
func PluginNamesNotSymlinked() []string {
	a := PluginNames()
	b := make([]string, 0, len(a))
	for _, plugin := range a {
		if !isPluginSymlinked(plugin) {
			b = append(b, plugin)
		}
	}
	return b
}

func isPluginSymlinked(plugin string) bool {
	path := filepath.Join(AppDir(), "node_modules", plugin)
	fi, err := os.Lstat(path)
	if err != nil {
		return false
	}
	return fi.Mode()&os.ModeSymlink != 0
}

// SetupBuiltinPlugins ensures all the builtinPlugins are installed
func SetupBuiltinPlugins() {
	pluginNames := difference(BuiltinPlugins, PluginNames())
	if len(pluginNames) == 0 {
		return
	}
	Err("heroku-cli: Installing core plugins...")
	if err := installPlugins(pluginNames...); err != nil {
		// retry once
		PrintError(gode.RemovePackages(pluginNames...), true)
		PrintError(gode.ClearCache(), true)
		Err("\rheroku-cli: Installing core plugins (retrying)...")
		ExitIfError(installPlugins(pluginNames...), true)
	}
	Errln(" done")
}

func difference(a, b []string) []string {
	res := make([]string, 0, len(a))
	for _, aa := range a {
		if !contains(b, aa) {
			res = append(res, aa)
		}
	}
	return res
}

func contains(arr []string, s string) bool {
	for _, a := range arr {
		if a == s {
			return true
		}
	}
	return false
}

func installPlugins(names ...string) error {
	for _, name := range names {
		lockPlugin(name)
	}
	defer func() {
		for _, name := range names {
			unlockPlugin(name)
		}
	}()
	err := gode.InstallPackages(names...)
	if err != nil {
		return err
	}
	plugins := make([]*Plugin, 0, len(names))
	for _, name := range names {
		plugin, err := ParsePlugin(name)
		if err != nil {
			return err
		}
		plugins = append(plugins, plugin)
	}
	AddPluginsToCache(plugins...)
	return nil
}

func pluginExists(plugin string) bool {
	exists, _ := fileExists(pluginPath(plugin))
	return exists
}

// directory location of plugin
func pluginPath(plugin string) string {
	return filepath.Join(AppDir(), "node_modules", plugin)
}

// lock a plugin for reading
func readLockPlugin(name string) {
	lockfile := updateLockPath + "." + name
	locked, err := golock.IsLocked(lockfile)
	LogIfError(err)
	if locked {
		lockPlugin(name)
		unlockPlugin(name)
	}
}

// lock a plugin for writing
func lockPlugin(name string) {
	LogIfError(golock.Lock(updateLockPath + "." + name))
}

// unlock a plugin
func unlockPlugin(name string) {
	LogIfError(golock.Unlock(updateLockPath + "." + name))
}
<|endoftext|>