<commit_before>package inputs

import (
	"context"
	"fmt"
	"github.com/influxdata/flux/memory"
	"github.com/influxdata/flux/semantic"
	"github.com/influxdata/flux/values"

	"github.com/influxdata/flux"
	"github.com/influxdata/flux/execute"
	"github.com/influxdata/flux/functions/inputs"
	"github.com/influxdata/flux/plan"
	"github.com/influxdata/platform"
	"github.com/influxdata/platform/query"
	"github.com/pkg/errors"
)

const DatabasesKind = "databases"

type DatabasesOpSpec struct {
}

func init() {
	databasesSignature := semantic.FunctionPolySignature{
		Return: flux.TableObjectType,
	}

	flux.RegisterFunction(DatabasesKind, createDatabasesOpSpec, databasesSignature)
	flux.RegisterOpSpec(DatabasesKind, newDatabasesOp)
	plan.RegisterProcedureSpec(DatabasesKind, newDatabasesProcedure, DatabasesKind)
}

func createDatabasesOpSpec(args flux.Arguments, a *flux.Administration) (flux.OperationSpec, error) {
	spec := new(DatabasesOpSpec)
	return spec, nil
}

func newDatabasesOp() flux.OperationSpec {
	return new(DatabasesOpSpec)
}

func (s *DatabasesOpSpec) Kind() flux.OperationKind {
	return DatabasesKind
}

type DatabasesProcedureSpec struct {
}

func newDatabasesProcedure(qs flux.OperationSpec, pa plan.Administration) (plan.ProcedureSpec, error) {
	_, ok := qs.(*DatabasesOpSpec)
	if !ok {
		return nil, fmt.Errorf("invalid spec type %T", qs)
	}

	return &DatabasesProcedureSpec{}, nil
}

func (s *DatabasesProcedureSpec) Kind() plan.ProcedureKind {
	return DatabasesKind
}

func (s *DatabasesProcedureSpec) Copy() plan.ProcedureSpec {
	ns := new(DatabasesProcedureSpec)
	return ns
}

func init() {
	execute.RegisterSource(DatabasesKind, createDatabasesSource)
}

type DatabasesDecoder struct {
	orgID     platform.ID
	deps      *DatabasesDependencies
	databases []*platform.DBRPMapping
	alloc     *memory.Allocator
	ctx       context.Context
}

func (bd *DatabasesDecoder) Connect() error {
	return nil
}

func (bd *DatabasesDecoder) Fetch() (bool, error) {

	b, _, err := bd.deps.DBRP.FindMany(bd.ctx, platform.DBRPMappingFilter{})
	if err != nil {
		return false, err
	}
	bd.databases = b
	return false, nil
}

func (bd *DatabasesDecoder) Decode() (flux.Table, error) {
	kb := execute.NewGroupKeyBuilder(nil)
	if len(bd.databases) == 0 {
		return nil, errors.New("no 1.x databases found")
	}
	kb.AddKeyValue("organizationID", values.NewString(bd.databases[0].OrganizationID.String()))
	gk, err := kb.Build()
	if err != nil {
		return nil, err
	}

	b := execute.NewColListTableBuilder(gk, bd.alloc)

	if _, err := b.AddCol(flux.ColMeta{
		Label: "organizationID",
		Type:  flux.TString,
	}); err != nil {
		return nil, err
	}
	if _, err := b.AddCol(flux.ColMeta{
		Label: "databaseName",
		Type:  flux.TString,
	}); err != nil {
		return nil, err
	}
	if _, err := b.AddCol(flux.ColMeta{
		Label: "retentionPolicy",
		Type:  flux.TString,
	}); err != nil {
		return nil, err
	}
	if _, err := b.AddCol(flux.ColMeta{
		Label: "retentionPeriod",
		Type:  flux.TInt,
	}); err != nil {
		return nil, err
	}
	if _, err := b.AddCol(flux.ColMeta{
		Label: "default",
		Type:  flux.TBool,
	}); err != nil {
		return nil, err
	}
	if _, err := b.AddCol(flux.ColMeta{
		Label: "bucketId",
		Type:  flux.TString,
	}); err != nil {
		return nil, err
	}

	for _, db := range bd.databases {
		if bucket, err := bd.deps.BucketLookup.FindBucketByID(bd.ctx, db.BucketID); err != nil {
			return nil, err
		} else {
			_ = b.AppendString(0, db.OrganizationID.String())
			_ = b.AppendString(1, db.Database)
			_ = b.AppendString(2, db.RetentionPolicy)
			_ = b.AppendInt(3, bucket.RetentionPeriod.Nanoseconds())
			_ = b.AppendBool(4, db.Default)
			_ = b.AppendString(5, db.BucketID.String())
		}
	}

	return b.Table()
}

func createDatabasesSource(prSpec plan.ProcedureSpec, dsid execute.DatasetID, a execute.Administration) (execute.Source, error) {
	_, ok := prSpec.(*DatabasesProcedureSpec)
	if !ok {
		return nil, fmt.Errorf("invalid spec type %T", prSpec)
	}

	// the dependencies used for FromKind are adequate for what we need here
	// so there's no need to inject custom dependencies for databases()
	deps := a.Dependencies()[DatabasesKind].(DatabasesDependencies)
	req := query.RequestFromContext(a.Context())
	if req == nil {
		return nil, errors.New("missing request on context")
	}
	orgID := req.OrganizationID

	bd := &DatabasesDecoder{orgID: orgID, deps: &deps, alloc: a.Allocator(), ctx: a.Context()}

	return inputs.CreateSourceFromDecoder(bd, dsid, a)

}

type DatabasesDependencies struct {
	DBRP         platform.DBRPMappingService
	BucketLookup platform.BucketService
}

func InjectDatabasesDependencies(depsMap execute.Dependencies, deps DatabasesDependencies) error {
	if deps.DBRP == nil {
		return errors.New("missing all databases lookup dependency")
	}

	if deps.BucketLookup == nil {
		return errors.New("missing buckets lookup dependency")
	}

	depsMap[DatabasesKind] = deps
	return nil
}
<commit_msg>fix(query): make databases() function embed plan.DefaultCost (#2079)<commit_after>package inputs

import (
	"context"
	"fmt"
	"github.com/influxdata/flux/memory"
	"github.com/influxdata/flux/semantic"
	"github.com/influxdata/flux/values"

	"github.com/influxdata/flux"
	"github.com/influxdata/flux/execute"
	"github.com/influxdata/flux/functions/inputs"
	"github.com/influxdata/flux/plan"
	"github.com/influxdata/platform"
	"github.com/influxdata/platform/query"
	"github.com/pkg/errors"
)

const DatabasesKind = "databases"

type DatabasesOpSpec struct {
}

func init() {
	databasesSignature := semantic.FunctionPolySignature{
		Return: flux.TableObjectType,
	}

	flux.RegisterFunction(DatabasesKind, createDatabasesOpSpec, databasesSignature)
	flux.RegisterOpSpec(DatabasesKind, newDatabasesOp)
	plan.RegisterProcedureSpec(DatabasesKind, newDatabasesProcedure, DatabasesKind)
}

func createDatabasesOpSpec(args flux.Arguments, a *flux.Administration) (flux.OperationSpec, error) {
	spec := new(DatabasesOpSpec)
	return spec, nil
}

func newDatabasesOp() flux.OperationSpec {
	return new(DatabasesOpSpec)
}

func (s *DatabasesOpSpec) Kind() flux.OperationKind {
	return DatabasesKind
}

type DatabasesProcedureSpec struct {
	plan.DefaultCost
}

func newDatabasesProcedure(qs flux.OperationSpec, pa plan.Administration) (plan.ProcedureSpec, error) {
	_, ok := qs.(*DatabasesOpSpec)
	if !ok {
		return nil, fmt.Errorf("invalid spec type %T", qs)
	}

	return &DatabasesProcedureSpec{}, nil
}

func (s *DatabasesProcedureSpec) Kind() plan.ProcedureKind {
	return DatabasesKind
}

func (s *DatabasesProcedureSpec) Copy() plan.ProcedureSpec {
	ns := new(DatabasesProcedureSpec)
	return ns
}

func init() {
	execute.RegisterSource(DatabasesKind, createDatabasesSource)
}

type DatabasesDecoder struct {
	orgID     platform.ID
	deps      *DatabasesDependencies
	databases []*platform.DBRPMapping
	alloc     *memory.Allocator
	ctx       context.Context
}

func (bd *DatabasesDecoder) Connect() error {
	return nil
}

func (bd *DatabasesDecoder) Fetch() (bool, error) {

	b, _, err := bd.deps.DBRP.FindMany(bd.ctx, platform.DBRPMappingFilter{})
	if err != nil {
		return false, err
	}
	bd.databases = b
	return false, nil
}

func (bd *DatabasesDecoder) Decode() (flux.Table, error) {
	kb := execute.NewGroupKeyBuilder(nil)
	if len(bd.databases) == 0 {
		return nil, errors.New("no 1.x databases found")
	}
	kb.AddKeyValue("organizationID", values.NewString(bd.databases[0].OrganizationID.String()))
	gk, err := kb.Build()
	if err != nil {
		return nil, err
	}

	b := execute.NewColListTableBuilder(gk, bd.alloc)

	if _, err := b.AddCol(flux.ColMeta{
		Label: "organizationID",
		Type:  flux.TString,
	}); err != nil {
		return nil, err
	}
	if _, err := b.AddCol(flux.ColMeta{
		Label: "databaseName",
		Type:  flux.TString,
	}); err != nil {
		return nil, err
	}
	if _, err := b.AddCol(flux.ColMeta{
		Label: "retentionPolicy",
		Type:  flux.TString,
	}); err != nil {
		return nil, err
	}
	if _, err := b.AddCol(flux.ColMeta{
		Label: "retentionPeriod",
		Type:  flux.TInt,
	}); err != nil {
		return nil, err
	}
	if _, err := b.AddCol(flux.ColMeta{
		Label: "default",
		Type:  flux.TBool,
	}); err != nil {
		return nil, err
	}
	if _, err := b.AddCol(flux.ColMeta{
		Label: "bucketId",
		Type:  flux.TString,
	}); err != nil {
		return nil, err
	}

	for _, db := range bd.databases {
		if bucket, err := bd.deps.BucketLookup.FindBucketByID(bd.ctx, db.BucketID); err != nil {
			return nil, err
		} else {
			_ = b.AppendString(0, db.OrganizationID.String())
			_ = b.AppendString(1, db.Database)
			_ = b.AppendString(2, db.RetentionPolicy)
			_ = b.AppendInt(3, bucket.RetentionPeriod.Nanoseconds())
			_ = b.AppendBool(4, db.Default)
			_ = b.AppendString(5, db.BucketID.String())
		}
	}

	return b.Table()
}

func createDatabasesSource(prSpec plan.ProcedureSpec, dsid execute.DatasetID, a execute.Administration) (execute.Source, error) {
	_, ok := prSpec.(*DatabasesProcedureSpec)
	if !ok {
		return nil, fmt.Errorf("invalid spec type %T", prSpec)
	}

	// the dependencies used for FromKind are adequate for what we need here
	// so there's no need to inject custom dependencies for databases()
	deps := a.Dependencies()[DatabasesKind].(DatabasesDependencies)
	req := query.RequestFromContext(a.Context())
	if req == nil {
		return nil, errors.New("missing request on context")
	}
	orgID := req.OrganizationID

	bd := &DatabasesDecoder{orgID: orgID, deps: &deps, alloc: a.Allocator(), ctx: a.Context()}

	return inputs.CreateSourceFromDecoder(bd, dsid, a)

}

type DatabasesDependencies struct {
	DBRP         platform.DBRPMappingService
	BucketLookup platform.BucketService
}

func InjectDatabasesDependencies(depsMap execute.Dependencies, deps DatabasesDependencies) error {
	if deps.DBRP == nil {
		return errors.New("missing all databases lookup dependency")
	}

	if deps.BucketLookup == nil {
		return errors.New("missing buckets lookup dependency")
	}

	depsMap[DatabasesKind] = deps
	return nil
}
<|endoftext|>


<commit_before>//go:build linux && cgo && !agent

package cluster

import (
	"context"
	"database/sql"
	"fmt"

	"github.com/lxc/lxd/lxd/db/query"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
)

// Code generation directives.
//
//go:generate -command mapper lxd-generate db mapper -t projects.mapper.go
//go:generate mapper reset -i -b "//go:build linux && cgo && !agent"
//
//go:generate mapper stmt -e project objects
//go:generate mapper stmt -e project objects-by-Name
//go:generate mapper stmt -e project objects-by-ID
//go:generate mapper stmt -e project create struct=Project
//go:generate mapper stmt -e project id
//go:generate mapper stmt -e project rename
//go:generate mapper stmt -e project update struct=Project
//go:generate mapper stmt -e project delete-by-Name
//
//go:generate mapper method -i -e project GetMany references=Config
//go:generate mapper method -i -e project GetOne struct=Project
//go:generate mapper method -i -e project Exists struct=Project
//go:generate mapper method -i -e project Create references=Config
//go:generate mapper method -i -e project ID struct=Project
//go:generate mapper method -i -e project Rename
//go:generate mapper method -i -e project DeleteOne-by-Name

// ProjectFeaturesDefaults are the features enabled by default on new projects.
// The features.networks won't be enabled by default until it becomes clear whether it is practical to run OVN on
// every system.
var ProjectFeaturesDefaults = []string{"features.images", "features.profiles", "features.storage.volumes"}

// ProjectFeatures are the features available to projects.
var ProjectFeatures = append(ProjectFeaturesDefaults, "features.networks")

// Project represents a LXD project.
type Project struct {
	ID          int
	Description string
	Name        string `db:"omit=update"`
}

// ProjectFilter specifies potential query parameter fields.
type ProjectFilter struct {
	ID   *int
	Name *string `db:"omit=update"` // If non-empty, return only the project with this name.
}

// ToAPI converts the database Project struct to an api.Project entry.
func (p *Project) ToAPI(ctx context.Context, tx *sql.Tx) (*api.Project, error) {
	apiProject := &api.Project{
		ProjectPut: api.ProjectPut{
			Description: p.Description,
		},
		Name: p.Name,
	}

	var err error
	apiProject.Config, err = GetProjectConfig(ctx, tx, p.ID)
	if err != nil {
		return nil, fmt.Errorf("Failed loading project config: %w", err)
	}

	return apiProject, nil
}

// ProjectHasProfiles is a helper to check if a project has the profiles
// feature enabled.
func ProjectHasProfiles(ctx context.Context, tx *sql.Tx, name string) (bool, error) {
	stmt := `
SELECT projects_config.value
  FROM projects_config
  JOIN projects ON projects.id=projects_config.project_id
 WHERE projects.name=? AND projects_config.key='features.profiles'
`
	values, err := query.SelectStrings(tx, stmt, name)
	if err != nil {
		return false, fmt.Errorf("Fetch project config: %w", err)
	}

	if len(values) == 0 {
		return false, nil
	}

	return shared.IsTrue(values[0]), nil
}

// GetProjectNames returns the names of all availablprojects.
func GetProjectNames(ctx context.Context, tx *sql.Tx) ([]string, error) {
	stmt := "SELECT name FROM projects"

	names, err := query.SelectStrings(tx, stmt)
	if err != nil {
		return nil, fmt.Errorf("Fetch project names: %w", err)
	}

	return names, nil
}

// GetProjectIDsToNames returns a map associating each prect ID to its
// project name.
func GetProjectIDsToNames(ctx context.Context, tx *sql.Tx) (map[int64]string, error) {
	stmt := "SELECT id, name FROM projects"

	rows, err := tx.Query(stmt)
	if err != nil {
		return nil, err
	}

	defer func() { _ = rows.Close() }()

	result := map[int64]string{}
	for i := 0; rows.Next(); i++ {
		var id int64
		var name string

		err := rows.Scan(&id, &name)
		if err != nil {
			return nil, err
		}

		result[id] = name
	}

	err = rows.Err()
	if err != nil {
		return nil, err
	}

	return result, nil
}

// ProjectHasImages is a helper to check if a project has the images
// feature enabled.
func ProjectHasImages(ctx context.Context, tx *sql.Tx, name string) (bool, error) {
	project, err := GetProject(ctx, tx, name)
	if err != nil {
		return false, fmt.Errorf("fetch project: %w", err)
	}

	config, err := GetProjectConfig(ctx, tx, project.ID)
	if err != nil {
		return false, err
	}

	enabled := shared.IsTrue(config["features.images"])

	return enabled, nil
}

// UpdateProject updates the project matching the given key parameters.
func UpdateProject(ctx context.Context, tx *sql.Tx, name string, object api.ProjectPut) error {
	id, err := GetProjectID(ctx, tx, name)
	if err != nil {
		return fmt.Errorf("Fetch project ID: %w", err)
	}

	stmt := Stmt(tx, projectUpdate)
	result, err := stmt.Exec(object.Description, id)
	if err != nil {
		return fmt.Errorf("Update project: %w", err)
	}

	n, err := result.RowsAffected()
	if err != nil {
		return fmt.Errorf("Fetch affected rows: %w", err)
	}

	if n != 1 {
		return fmt.Errorf("Query updated %d rows instead of 1", n)
	}

	// Clear config.
	_, err = tx.Exec(`
DELETE FROM projects_config WHERE projects_config.project_id = ?
`, id)
	if err != nil {
		return fmt.Errorf("Delete project config: %w", err)
	}

	err = UpdateConfig(ctx, tx, "project", int(id), object.Config)
	if err != nil {
		return fmt.Errorf("Insert config for project: %w", err)
	}

	return nil
}

// InitProjectWithoutImages updates populates the images_profiles table with
// all images from the default project when a project is created with
// features.images=false.
func InitProjectWithoutImages(ctx context.Context, tx *sql.Tx, project string) error {
	defaultProfileID, err := GetProfileID(ctx, tx, project, "default")
	if err != nil {
		return fmt.Errorf("Fetch project ID: %w", err)
	}

	stmt := `INSERT INTO images_profiles (image_id, profile_id)
	SELECT images.id, ? FROM images WHERE project_id=1`
	_, err = tx.Exec(stmt, defaultProfileID)
	return err
}
<commit_msg>lxd/db/cluster/projects: Adds "features.storage.buckets" to ProjectFeaturesDefaults<commit_after>//go:build linux && cgo && !agent

package cluster

import (
	"context"
	"database/sql"
	"fmt"

	"github.com/lxc/lxd/lxd/db/query"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
)

// Code generation directives.
//
//go:generate -command mapper lxd-generate db mapper -t projects.mapper.go
//go:generate mapper reset -i -b "//go:build linux && cgo && !agent"
//
//go:generate mapper stmt -e project objects
//go:generate mapper stmt -e project objects-by-Name
//go:generate mapper stmt -e project objects-by-ID
//go:generate mapper stmt -e project create struct=Project
//go:generate mapper stmt -e project id
//go:generate mapper stmt -e project rename
//go:generate mapper stmt -e project update struct=Project
//go:generate mapper stmt -e project delete-by-Name
//
//go:generate mapper method -i -e project GetMany references=Config
//go:generate mapper method -i -e project GetOne struct=Project
//go:generate mapper method -i -e project Exists struct=Project
//go:generate mapper method -i -e project Create references=Config
//go:generate mapper method -i -e project ID struct=Project
//go:generate mapper method -i -e project Rename
//go:generate mapper method -i -e project DeleteOne-by-Name

// ProjectFeaturesDefaults are the features enabled by default on new projects.
// The features.networks won't be enabled by default until it becomes clear whether it is practical to run OVN on
// every system.
var ProjectFeaturesDefaults = []string{"features.images", "features.profiles", "features.storage.volumes", "features.storage.buckets"}

// ProjectFeatures are the features available to projects.
var ProjectFeatures = append(ProjectFeaturesDefaults, "features.networks")

// Project represents a LXD project.
type Project struct {
	ID          int
	Description string
	Name        string `db:"omit=update"`
}

// ProjectFilter specifies potential query parameter fields.
type ProjectFilter struct {
	ID   *int
	Name *string `db:"omit=update"` // If non-empty, return only the project with this name.
}

// ToAPI converts the database Project struct to an api.Project entry.
func (p *Project) ToAPI(ctx context.Context, tx *sql.Tx) (*api.Project, error) {
	apiProject := &api.Project{
		ProjectPut: api.ProjectPut{
			Description: p.Description,
		},
		Name: p.Name,
	}

	var err error
	apiProject.Config, err = GetProjectConfig(ctx, tx, p.ID)
	if err != nil {
		return nil, fmt.Errorf("Failed loading project config: %w", err)
	}

	return apiProject, nil
}

// ProjectHasProfiles is a helper to check if a project has the profiles
// feature enabled.
func ProjectHasProfiles(ctx context.Context, tx *sql.Tx, name string) (bool, error) {
	stmt := `
SELECT projects_config.value
  FROM projects_config
  JOIN projects ON projects.id=projects_config.project_id
 WHERE projects.name=? AND projects_config.key='features.profiles'
`
	values, err := query.SelectStrings(tx, stmt, name)
	if err != nil {
		return false, fmt.Errorf("Fetch project config: %w", err)
	}

	if len(values) == 0 {
		return false, nil
	}

	return shared.IsTrue(values[0]), nil
}

// GetProjectNames returns the names of all availablprojects.
func GetProjectNames(ctx context.Context, tx *sql.Tx) ([]string, error) {
	stmt := "SELECT name FROM projects"

	names, err := query.SelectStrings(tx, stmt)
	if err != nil {
		return nil, fmt.Errorf("Fetch project names: %w", err)
	}

	return names, nil
}

// GetProjectIDsToNames returns a map associating each prect ID to its
// project name.
func GetProjectIDsToNames(ctx context.Context, tx *sql.Tx) (map[int64]string, error) {
	stmt := "SELECT id, name FROM projects"

	rows, err := tx.Query(stmt)
	if err != nil {
		return nil, err
	}

	defer func() { _ = rows.Close() }()

	result := map[int64]string{}
	for i := 0; rows.Next(); i++ {
		var id int64
		var name string

		err := rows.Scan(&id, &name)
		if err != nil {
			return nil, err
		}

		result[id] = name
	}

	err = rows.Err()
	if err != nil {
		return nil, err
	}

	return result, nil
}

// ProjectHasImages is a helper to check if a project has the images
// feature enabled.
func ProjectHasImages(ctx context.Context, tx *sql.Tx, name string) (bool, error) {
	project, err := GetProject(ctx, tx, name)
	if err != nil {
		return false, fmt.Errorf("fetch project: %w", err)
	}

	config, err := GetProjectConfig(ctx, tx, project.ID)
	if err != nil {
		return false, err
	}

	enabled := shared.IsTrue(config["features.images"])

	return enabled, nil
}

// UpdateProject updates the project matching the given key parameters.
func UpdateProject(ctx context.Context, tx *sql.Tx, name string, object api.ProjectPut) error {
	id, err := GetProjectID(ctx, tx, name)
	if err != nil {
		return fmt.Errorf("Fetch project ID: %w", err)
	}

	stmt := Stmt(tx, projectUpdate)
	result, err := stmt.Exec(object.Description, id)
	if err != nil {
		return fmt.Errorf("Update project: %w", err)
	}

	n, err := result.RowsAffected()
	if err != nil {
		return fmt.Errorf("Fetch affected rows: %w", err)
	}

	if n != 1 {
		return fmt.Errorf("Query updated %d rows instead of 1", n)
	}

	// Clear config.
	_, err = tx.Exec(`
DELETE FROM projects_config WHERE projects_config.project_id = ?
`, id)
	if err != nil {
		return fmt.Errorf("Delete project config: %w", err)
	}

	err = UpdateConfig(ctx, tx, "project", int(id), object.Config)
	if err != nil {
		return fmt.Errorf("Insert config for project: %w", err)
	}

	return nil
}

// InitProjectWithoutImages updates populates the images_profiles table with
// all images from the default project when a project is created with
// features.images=false.
func InitProjectWithoutImages(ctx context.Context, tx *sql.Tx, project string) error {
	defaultProfileID, err := GetProfileID(ctx, tx, project, "default")
	if err != nil {
		return fmt.Errorf("Fetch project ID: %w", err)
	}

	stmt := `INSERT INTO images_profiles (image_id, profile_id)
	SELECT images.id, ? FROM images WHERE project_id=1`
	_, err = tx.Exec(stmt, defaultProfileID)
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"github.com/lxc/lxd/lxd/cluster/request"
	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/revert"
	"github.com/lxc/lxd/lxd/state"
	storagePools "github.com/lxc/lxd/lxd/storage"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/logger"
)

// storagePoolDBCreate creates a storage pool DB entry and returns the created Pool ID.
func storagePoolDBCreate(s *state.State, poolName string, poolDescription string, driver string, config map[string]string) (int64, error) {
	// Check that the storage pool does not already exist.
	_, err := s.Cluster.GetStoragePoolID(poolName)
	if err == nil {
		return -1, fmt.Errorf("The storage pool already exists: %w", db.ErrAlreadyDefined)
	}

	// Make sure that we don't pass a nil to the next function.
	if config == nil {
		config = map[string]string{}
	}

	err = storagePoolValidate(s, poolName, driver, config)
	if err != nil {
		return -1, err
	}

	// Fill in the defaults.
	err = storagePoolFillDefault(poolName, driver, config)
	if err != nil {
		return -1, err
	}

	// Create the database entry for the storage pool.
	id, err := dbStoragePoolCreateAndUpdateCache(s, poolName, poolDescription, driver, config)
	if err != nil {
		return -1, fmt.Errorf("Error inserting %s into database: %w", poolName, err)
	}

	return id, nil
}

func storagePoolValidate(s *state.State, poolName string, driverName string, config map[string]string) error {
	poolType, err := storagePools.LoadByType(s, driverName)
	if err != nil {
		return err
	}

	// Check if the storage pool name is valid.
	err = poolType.ValidateName(poolName)
	if err != nil {
		return err
	}

	// Validate the requested storage pool configuration.
	err = poolType.Validate(config)
	if err != nil {
		return err
	}

	return nil
}

func storagePoolCreateGlobal(state *state.State, req api.StoragePoolsPost, clientType request.ClientType) error {
	// Create the database entry.
	id, err := storagePoolDBCreate(state, req.Name, req.Description, req.Driver, req.Config)
	if err != nil {
		return err
	}

	// Define a function which reverts everything.  Defer this function
	// so that it doesn't need to be explicitly called in every failing
	// return path. Track whether or not we want to undo the changes
	// using a closure.
	tryUndo := true
	defer func() {
		if !tryUndo {
			return
		}

		dbStoragePoolDeleteAndUpdateCache(state, req.Name)
	}()

	_, err = storagePoolCreateLocal(state, id, req, clientType)
	if err != nil {
		return err
	}

	tryUndo = false
	return nil
}

// This performs local pool setup and updates DB record if config was changed during pool setup.
// Returns resulting config.
func storagePoolCreateLocal(state *state.State, poolID int64, req api.StoragePoolsPost, clientType request.ClientType) (map[string]string, error) {
	// Setup revert.
	revert := revert.New()
	defer revert.Fail()

	// Make a copy of the req for later diff.
	var updatedReq api.StoragePoolsPost
	shared.DeepCopy(&req, &updatedReq)

	// Make sure that we don't pass a nil to the next function.
	if updatedReq.Config == nil {
		updatedReq.Config = map[string]string{}
	}

	// Fill in the node specific defaults.
	err := storagePoolFillDefault(updatedReq.Name, updatedReq.Driver, updatedReq.Config)
	if err != nil {
		return nil, err
	}

	configDiff, _ := storagePools.ConfigDiff(req.Config, updatedReq.Config)
	if len(configDiff) > 0 {
		// Update the database entry for the storage pool.
		err = state.Cluster.UpdateStoragePool(req.Name, req.Description, updatedReq.Config)
		if err != nil {
			return nil, fmt.Errorf("Error updating storage pool config after local fill defaults for %q: %w", req.Name, err)
		}
	}

	// Load pool record.
	pool, err := storagePools.LoadByName(state, updatedReq.Name)
	if err != nil {
		return nil, err
	}

	if pool.LocalStatus() == api.NetworkStatusCreated {
		logger.Debug("Skipping local storage pool create as already created", logger.Ctx{"pool": pool.Name()})

		return pool.Driver().Config(), nil
	}

	// Create the pool.
	err = pool.Create(clientType, nil)
	if err != nil {
		return nil, err
	}

	revert.Add(func() { pool.Delete(clientType, nil) })

	// Mount the pool.
	_, err = pool.Mount()
	if err != nil {
		return nil, err
	}

	// In case the storage pool config was changed during the pool creation, we need to update the database to
	// reflect this change. This can e.g. happen, when we create a loop file image. This means we append ".img"
	// to the path the user gave us and update the config in the storage callback. So diff the config here to
	// see if something like this has happened.
	configDiff, _ = storagePools.ConfigDiff(updatedReq.Config, pool.Driver().Config())
	if len(configDiff) > 0 {
		// Update the database entry for the storage pool.
		err = state.Cluster.UpdateStoragePool(req.Name, req.Description, pool.Driver().Config())
		if err != nil {
			return nil, fmt.Errorf("Error updating storage pool config after local create for %q: %w", req.Name, err)
		}
	}

	// Set storage pool node to storagePoolCreated.
	err = state.Cluster.Transaction(func(tx *db.ClusterTx) error {
		return tx.StoragePoolNodeCreated(poolID)
	})
	if err != nil {
		return nil, err
	}
	logger.Debug("Marked storage pool local status as created", logger.Ctx{"pool": req.Name})

	revert.Success()
	return pool.Driver().Config(), nil
}

// Helper around the low-level DB API, which also updates the driver names cache.
func dbStoragePoolCreateAndUpdateCache(s *state.State, poolName string, poolDescription string, poolDriver string, poolConfig map[string]string) (int64, error) {
	id, err := s.Cluster.CreateStoragePool(poolName, poolDescription, poolDriver, poolConfig)
	if err != nil {
		return id, err
	}

	// Update the storage drivers cache in api_1.0.go.
	storagePoolDriversCacheUpdate(s)

	return id, nil
}

// Helper around the low-level DB API, which also updates the driver names
// cache.
func dbStoragePoolDeleteAndUpdateCache(s *state.State, poolName string) error {
	_, err := s.Cluster.RemoveStoragePool(poolName)
	if err != nil {
		return err
	}

	// Update the storage drivers cache in api_1.0.go.
	storagePoolDriversCacheUpdate(s)

	return err
}
<commit_msg>lxd/storage/pools/utils: Use revert in storagePoolCreateGlobal<commit_after>package main

import (
	"fmt"

	"github.com/lxc/lxd/lxd/cluster/request"
	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/revert"
	"github.com/lxc/lxd/lxd/state"
	storagePools "github.com/lxc/lxd/lxd/storage"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/logger"
)

// storagePoolDBCreate creates a storage pool DB entry and returns the created Pool ID.
func storagePoolDBCreate(s *state.State, poolName string, poolDescription string, driver string, config map[string]string) (int64, error) {
	// Check that the storage pool does not already exist.
	_, err := s.Cluster.GetStoragePoolID(poolName)
	if err == nil {
		return -1, fmt.Errorf("The storage pool already exists: %w", db.ErrAlreadyDefined)
	}

	// Make sure that we don't pass a nil to the next function.
	if config == nil {
		config = map[string]string{}
	}

	err = storagePoolValidate(s, poolName, driver, config)
	if err != nil {
		return -1, err
	}

	// Fill in the defaults.
	err = storagePoolFillDefault(poolName, driver, config)
	if err != nil {
		return -1, err
	}

	// Create the database entry for the storage pool.
	id, err := dbStoragePoolCreateAndUpdateCache(s, poolName, poolDescription, driver, config)
	if err != nil {
		return -1, fmt.Errorf("Error inserting %s into database: %w", poolName, err)
	}

	return id, nil
}

func storagePoolValidate(s *state.State, poolName string, driverName string, config map[string]string) error {
	poolType, err := storagePools.LoadByType(s, driverName)
	if err != nil {
		return err
	}

	// Check if the storage pool name is valid.
	err = poolType.ValidateName(poolName)
	if err != nil {
		return err
	}

	// Validate the requested storage pool configuration.
	err = poolType.Validate(config)
	if err != nil {
		return err
	}

	return nil
}

func storagePoolCreateGlobal(state *state.State, req api.StoragePoolsPost, clientType request.ClientType) error {
	// Create the database entry.
	id, err := storagePoolDBCreate(state, req.Name, req.Description, req.Driver, req.Config)
	if err != nil {
		return err
	}

	// Define a function which reverts everything.  Defer this function
	// so that it doesn't need to be explicitly called in every failing
	// return path. Track whether or not we want to undo the changes
	// using a closure.
	revert := revert.New()
	defer revert.Fail()

	revert.Add(func() { dbStoragePoolDeleteAndUpdateCache(state, req.Name) })

	_, err = storagePoolCreateLocal(state, id, req, clientType)
	if err != nil {
		return err
	}

	revert.Success()
	return nil
}

// This performs local pool setup and updates DB record if config was changed during pool setup.
// Returns resulting config.
func storagePoolCreateLocal(state *state.State, poolID int64, req api.StoragePoolsPost, clientType request.ClientType) (map[string]string, error) {
	// Setup revert.
	revert := revert.New()
	defer revert.Fail()

	// Make a copy of the req for later diff.
	var updatedReq api.StoragePoolsPost
	shared.DeepCopy(&req, &updatedReq)

	// Make sure that we don't pass a nil to the next function.
	if updatedReq.Config == nil {
		updatedReq.Config = map[string]string{}
	}

	// Fill in the node specific defaults.
	err := storagePoolFillDefault(updatedReq.Name, updatedReq.Driver, updatedReq.Config)
	if err != nil {
		return nil, err
	}

	configDiff, _ := storagePools.ConfigDiff(req.Config, updatedReq.Config)
	if len(configDiff) > 0 {
		// Update the database entry for the storage pool.
		err = state.Cluster.UpdateStoragePool(req.Name, req.Description, updatedReq.Config)
		if err != nil {
			return nil, fmt.Errorf("Error updating storage pool config after local fill defaults for %q: %w", req.Name, err)
		}
	}

	// Load pool record.
	pool, err := storagePools.LoadByName(state, updatedReq.Name)
	if err != nil {
		return nil, err
	}

	if pool.LocalStatus() == api.NetworkStatusCreated {
		logger.Debug("Skipping local storage pool create as already created", logger.Ctx{"pool": pool.Name()})

		return pool.Driver().Config(), nil
	}

	// Create the pool.
	err = pool.Create(clientType, nil)
	if err != nil {
		return nil, err
	}

	revert.Add(func() { pool.Delete(clientType, nil) })

	// Mount the pool.
	_, err = pool.Mount()
	if err != nil {
		return nil, err
	}

	// In case the storage pool config was changed during the pool creation, we need to update the database to
	// reflect this change. This can e.g. happen, when we create a loop file image. This means we append ".img"
	// to the path the user gave us and update the config in the storage callback. So diff the config here to
	// see if something like this has happened.
	configDiff, _ = storagePools.ConfigDiff(updatedReq.Config, pool.Driver().Config())
	if len(configDiff) > 0 {
		// Update the database entry for the storage pool.
		err = state.Cluster.UpdateStoragePool(req.Name, req.Description, pool.Driver().Config())
		if err != nil {
			return nil, fmt.Errorf("Error updating storage pool config after local create for %q: %w", req.Name, err)
		}
	}

	// Set storage pool node to storagePoolCreated.
	err = state.Cluster.Transaction(func(tx *db.ClusterTx) error {
		return tx.StoragePoolNodeCreated(poolID)
	})
	if err != nil {
		return nil, err
	}
	logger.Debug("Marked storage pool local status as created", logger.Ctx{"pool": req.Name})

	revert.Success()
	return pool.Driver().Config(), nil
}

// Helper around the low-level DB API, which also updates the driver names cache.
func dbStoragePoolCreateAndUpdateCache(s *state.State, poolName string, poolDescription string, poolDriver string, poolConfig map[string]string) (int64, error) {
	id, err := s.Cluster.CreateStoragePool(poolName, poolDescription, poolDriver, poolConfig)
	if err != nil {
		return id, err
	}

	// Update the storage drivers cache in api_1.0.go.
	storagePoolDriversCacheUpdate(s)

	return id, nil
}

// Helper around the low-level DB API, which also updates the driver names
// cache.
func dbStoragePoolDeleteAndUpdateCache(s *state.State, poolName string) error {
	_, err := s.Cluster.RemoveStoragePool(poolName)
	if err != nil {
		return err
	}

	// Update the storage drivers cache in api_1.0.go.
	storagePoolDriversCacheUpdate(s)

	return err
}
<|endoftext|>


<commit_before>package makeupdater

// Note this package is exclusively compatible with Drupal 7 make files.

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
)

func replaceTextInFile(fullPath string, oldString string, newString string) {
	read, err := ioutil.ReadFile(fullPath)
	if err != nil {
		panic(err)
	}
	newContents := strings.Replace(string(read), oldString, newString, -1)
	err = ioutil.WriteFile(fullPath, []byte(newContents), 0)
	if err != nil {
		panic(err)
	}
}

func removeChar(input string, chars ...string) string {
	for _, value := range chars {
		input = strings.Replace(input, value, "", -1)
	}
	return input
}

func UpdateMake(fullpath string) {
	// Update the version numbers in a specified make file
	projects := GetProjectsFromMake(fullpath)
	count := 0
	for _, project := range projects {
		if project != "" {

			catCmd := "cat " + fullpath + " | grep \"projects\\[" + project + "\\]\" | grep version | cut -d '=' -f2"
			z, _ := exec.Command("sh", "-c", catCmd).Output()
			versionOld := removeChar(string(z), " ", "\"", "\n")
			x, _ := exec.Command("sh", "-c", "drush pm-releases --pipe "+project+" | grep Recommended | cut -d',' -f2").Output()
			versionNew := removeChar(string(x), " ", "7.x-", "\"", "\n", "[", "]")
			if versionOld != versionNew && versionOld != "" && versionNew != "" {
				fmt.Printf("Replacing %v v%v with v%v\n", project, versionOld, versionNew)
				count++
				replaceTextInFile(fullpath, fmt.Sprintf("projects[%v][version] = \"%v\"\n", project, versionOld), fmt.Sprintf("projects[%v][version] = \"%v\"\n", project, versionNew))
			}
		}
	}
	if count != 0 {
		fmt.Println(fullpath, "is already up to date.")
	}
}

func GetProjectsFromMake(fullpath string) []string {
	// Returns a list of projects from a given make file
	Projects := []string{}
	catCmd := "cat " + fullpath + " | grep projects | cut -d'[' -f2 | cut -d']' -f1 | uniq | sort"
	y, _ := exec.Command("sh", "-c", catCmd).Output()
	rawProjects := strings.Split(string(y), "\n")
	for _, project := range rawProjects {
		project = strings.Replace(project, " ", "", -1)
		if project != "" {
			Projects = append(Projects, project)
		}
	}
	return Projects
}

func GenerateMake(Projects []string, File string) {
	// Takes a []string of projects and writes out a make file
	// Modules are added with the latest recommended version.
	headerLines := []string{}
	headerLines = append(headerLines, "; Generated by make-updater")
	headerLines = append(headerLines, "; Script created by Fubarhouse")
	headerLines = append(headerLines, "; Toolkit available at github.com/fubarhouse/golang-drush/...")
	headerLines = append(headerLines, "core = 7.x")
	headerLines = append(headerLines, "api = 2")
	headerLines = append(headerLines, "")

	// Rewrite core, if core is in the original Projects list.

	for _, Project := range Projects {
		coreAppended := 0
		if Project == "drupal" {
			if coreAppended == 0 {
				headerLines = append(headerLines, "; core")
				x, _ := exec.Command("sh", "-c", "drush pm-releases --pipe drupal | grep Recommended | cut -d',' -f2").Output()
				ProjectVersion := removeChar(string(x), " ", "7.x-", "\"", "\n", "[", "]")
				headerLines = append(headerLines, "projects[drupal][type] = \"core\"")
				headerLines = append(headerLines, fmt.Sprintf("projects[drupal][version] = \"%v\"", ProjectVersion))
				headerLines = append(headerLines, "projects[drupal][download][type] = \"get\"")
				headerLines = append(headerLines, fmt.Sprintf("projects[drupal][download][url] = \"https://ftp.drupal.org/files/projects/drupal-%v.tar.gz\"", ProjectVersion))
				headerLines = append(headerLines, "")
				coreAppended++
			}
		}
	}

	// Rewrite contrib
	headerLines = append(headerLines, "; modules")
	headerLines = append(headerLines, "defaults[projects][subdir] = contrib")
	headerLines = append(headerLines, "")

	for _, Project := range Projects {

		if Project != "drupal" {
			x, y := exec.Command("sh", "-c", "drush pm-releases --pipe "+Project+" | grep Recommended | cut -d',' -f2").Output()
			if y == nil {
				ProjectVersion := removeChar(string(x), " ", "7.x-", "\"", "\n", "[", "]")
				headerLines = append(headerLines, fmt.Sprintf("projects[%v][version] = \"%v\"", Project, ProjectVersion))
			}
		}
	}

	// Print to path File

	newFile, _ := os.Create(File)
	for _, line := range headerLines {
		fmt.Fprintln(newFile, line)
	}
	newFile.Sync()
	defer newFile.Close()

}
<commit_msg>prevent the term projects from becoming a project in generated make files.<commit_after>package makeupdater

// Note this package is exclusively compatible with Drupal 7 make files.

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
)

func replaceTextInFile(fullPath string, oldString string, newString string) {
	read, err := ioutil.ReadFile(fullPath)
	if err != nil {
		panic(err)
	}
	newContents := strings.Replace(string(read), oldString, newString, -1)
	err = ioutil.WriteFile(fullPath, []byte(newContents), 0)
	if err != nil {
		panic(err)
	}
}

func removeChar(input string, chars ...string) string {
	for _, value := range chars {
		input = strings.Replace(input, value, "", -1)
	}
	return input
}

func UpdateMake(fullpath string) {
	// Update the version numbers in a specified make file
	projects := GetProjectsFromMake(fullpath)
	count := 0
	for _, project := range projects {
		if project != "" {

			catCmd := "cat " + fullpath + " | grep \"projects\\[" + project + "\\]\" | grep version | cut -d '=' -f2"
			z, _ := exec.Command("sh", "-c", catCmd).Output()
			versionOld := removeChar(string(z), " ", "\"", "\n")
			x, _ := exec.Command("sh", "-c", "drush pm-releases --pipe "+project+" | grep Recommended | cut -d',' -f2").Output()
			versionNew := removeChar(string(x), " ", "7.x-", "\"", "\n", "[", "]")
			if versionOld != versionNew && versionOld != "" && versionNew != "" {
				fmt.Printf("Replacing %v v%v with v%v\n", project, versionOld, versionNew)
				count++
				replaceTextInFile(fullpath, fmt.Sprintf("projects[%v][version] = \"%v\"\n", project, versionOld), fmt.Sprintf("projects[%v][version] = \"%v\"\n", project, versionNew))
			}
		}
	}
	if count != 0 {
		fmt.Println(fullpath, "is already up to date.")
	}
}

func GetProjectsFromMake(fullpath string) []string {
	// Returns a list of projects from a given make file
	Projects := []string{}
	catCmd := "cat " + fullpath + " | grep projects | cut -d'[' -f2 | cut -d']' -f1 | uniq | sort"
	y, _ := exec.Command("sh", "-c", catCmd).Output()
	rawProjects := strings.Split(string(y), "\n")
	for _, project := range rawProjects {
		project = strings.Replace(project, " ", "", -1)
		if project != "" && project != "projects" {
			Projects = append(Projects, project)
		}
	}
	return Projects
}

func GenerateMake(Projects []string, File string) {
	// Takes a []string of projects and writes out a make file
	// Modules are added with the latest recommended version.
	headerLines := []string{}
	headerLines = append(headerLines, "; Generated by make-updater")
	headerLines = append(headerLines, "; Script created by Fubarhouse")
	headerLines = append(headerLines, "; Toolkit available at github.com/fubarhouse/golang-drush/...")
	headerLines = append(headerLines, "core = 7.x")
	headerLines = append(headerLines, "api = 2")
	headerLines = append(headerLines, "")

	// Rewrite core, if core is in the original Projects list.

	for _, Project := range Projects {
		coreAppended := 0
		if Project == "drupal" {
			if coreAppended == 0 {
				headerLines = append(headerLines, "; core")
				x, _ := exec.Command("sh", "-c", "drush pm-releases --pipe drupal | grep Recommended | cut -d',' -f2").Output()
				ProjectVersion := removeChar(string(x), " ", "7.x-", "\"", "\n", "[", "]")
				headerLines = append(headerLines, "projects[drupal][type] = \"core\"")
				headerLines = append(headerLines, fmt.Sprintf("projects[drupal][version] = \"%v\"", ProjectVersion))
				headerLines = append(headerLines, "projects[drupal][download][type] = \"get\"")
				headerLines = append(headerLines, fmt.Sprintf("projects[drupal][download][url] = \"https://ftp.drupal.org/files/projects/drupal-%v.tar.gz\"", ProjectVersion))
				headerLines = append(headerLines, "")
				coreAppended++
			}
		}
	}

	// Rewrite contrib
	headerLines = append(headerLines, "; modules")
	headerLines = append(headerLines, "defaults[projects][subdir] = contrib")
	headerLines = append(headerLines, "")

	for _, Project := range Projects {

		if Project != "drupal" {
			x, y := exec.Command("sh", "-c", "drush pm-releases --pipe "+Project+" | grep Recommended | cut -d',' -f2").Output()
			if y == nil {
				ProjectVersion := removeChar(string(x), " ", "7.x-", "\"", "\n", "[", "]")
				headerLines = append(headerLines, fmt.Sprintf("projects[%v][version] = \"%v\"", Project, ProjectVersion))
			}
		}
	}

	// Print to path File

	newFile, _ := os.Create(File)
	for _, line := range headerLines {
		fmt.Fprintln(newFile, line)
	}
	newFile.Sync()
	defer newFile.Close()

}
<|endoftext|>


<commit_before>package manifest

import (
	"encoding/json"
	"time"

	"github.com/containers/image/pkg/strslice"
	"github.com/containers/image/types"
	"github.com/opencontainers/go-digest"
	"github.com/pkg/errors"
)

// Schema2Descriptor is a “descriptor” in docker/distribution schema 2.
type Schema2Descriptor struct {
	MediaType string        `json:"mediaType"`
	Size      int64         `json:"size"`
	Digest    digest.Digest `json:"digest"`
	URLs      []string      `json:"urls,omitempty"`
}

// BlobInfoFromSchema2Descriptor returns a types.BlobInfo based on the input schema 2 descriptor.
func BlobInfoFromSchema2Descriptor(desc Schema2Descriptor) types.BlobInfo {
	return types.BlobInfo{
		Digest:    desc.Digest,
		Size:      desc.Size,
		URLs:      desc.URLs,
		MediaType: desc.MediaType,
	}
}

// Schema2 is a manifest in docker/distribution schema 2.
type Schema2 struct {
	SchemaVersion     int                 `json:"schemaVersion"`
	MediaType         string              `json:"mediaType"`
	ConfigDescriptor  Schema2Descriptor   `json:"config"`
	LayersDescriptors []Schema2Descriptor `json:"layers"`
}

// Schema2Port is a Port, a string containing port number and protocol in the
// format "80/tcp", from docker/go-connections/nat.
type Schema2Port string

// Schema2PortSet is a PortSet, a collection of structs indexed by Port, from
// docker/go-connections/nat.
type Schema2PortSet map[Schema2Port]struct{}

// Schema2HealthConfig is a HealthConfig, which holds configuration settings
// for the HEALTHCHECK feature, from docker/docker/api/types/container.
type Schema2HealthConfig struct {
	// Test is the test to perform to check that the container is healthy.
	// An empty slice means to inherit the default.
	// The options are:
	// {} : inherit healthcheck
	// {"NONE"} : disable healthcheck
	// {"CMD", args...} : exec arguments directly
	// {"CMD-SHELL", command} : run command with system's default shell
	Test []string `json:",omitempty"`

	// Zero means to inherit. Durations are expressed as integer nanoseconds.
	Interval time.Duration `json:",omitempty"` // Interval is the time to wait between checks.
	Timeout  time.Duration `json:",omitempty"` // Timeout is the time to wait before considering the check to have hung.

	// Retries is the number of consecutive failures needed to consider a container as unhealthy.
	// Zero means inherit.
	Retries int `json:",omitempty"`
}

// Schema2Config is a Config in docker/docker/api/types/container.
type Schema2Config struct {
	Hostname        string               // Hostname
	Domainname      string               // Domainname
	User            string               // User that will run the command(s) inside the container, also support user:group
	AttachStdin     bool                 // Attach the standard input, makes possible user interaction
	AttachStdout    bool                 // Attach the standard output
	AttachStderr    bool                 // Attach the standard error
	ExposedPorts    Schema2PortSet       `json:",omitempty"` // List of exposed ports
	Tty             bool                 // Attach standard streams to a tty, including stdin if it is not closed.
	OpenStdin       bool                 // Open stdin
	StdinOnce       bool                 // If true, close stdin after the 1 attached client disconnects.
	Env             []string             // List of environment variable to set in the container
	Cmd             strslice.StrSlice    // Command to run when starting the container
	Healthcheck     *Schema2HealthConfig `json:",omitempty"` // Healthcheck describes how to check the container is healthy
	ArgsEscaped     bool                 `json:",omitempty"` // True if command is already escaped (Windows specific)
	Image           string               // Name of the image as it was passed by the operator (e.g. could be symbolic)
	Volumes         map[string]struct{}  // List of volumes (mounts) used for the container
	WorkingDir      string               // Current directory (PWD) in the command will be launched
	Entrypoint      strslice.StrSlice    // Entrypoint to run when starting the container
	NetworkDisabled bool                 `json:",omitempty"` // Is network disabled
	MacAddress      string               `json:",omitempty"` // Mac Address of the container
	OnBuild         []string             // ONBUILD metadata that were defined on the image Dockerfile
	Labels          map[string]string    // List of labels set to this container
	StopSignal      string               `json:",omitempty"` // Signal to stop a container
	StopTimeout     *int                 `json:",omitempty"` // Timeout (in seconds) to stop a container
	Shell           strslice.StrSlice    `json:",omitempty"` // Shell for shell-form of RUN, CMD, ENTRYPOINT
}

// Schema2V1Image is a V1Image in docker/docker/image.
type Schema2V1Image struct {
	// ID is a unique 64 character identifier of the image
	ID string `json:"id,omitempty"`
	// Parent is the ID of the parent image
	Parent string `json:"parent,omitempty"`
	// Comment is the commit message that was set when committing the image
	Comment string `json:"comment,omitempty"`
	// Created is the timestamp at which the image was created
	Created time.Time `json:"created"`
	// Container is the id of the container used to commit
	Container string `json:"container,omitempty"`
	// ContainerConfig is the configuration of the container that is committed into the image
	ContainerConfig Schema2Config `json:"container_config,omitempty"`
	// DockerVersion specifies the version of Docker that was used to build the image
	DockerVersion string `json:"docker_version,omitempty"`
	// Author is the name of the author that was specified when committing the image
	Author string `json:"author,omitempty"`
	// Config is the configuration of the container received from the client
	Config *Schema2Config `json:"config,omitempty"`
	// Architecture is the hardware that the image is build and runs on
	Architecture string `json:"architecture,omitempty"`
	// OS is the operating system used to build and run the image
	OS string `json:"os,omitempty"`
	// Size is the total size of the image including all layers it is composed of
	Size int64 `json:",omitempty"`
}

// Schema2RootFS is a description of how to build up an image's root filesystem, from docker/docker/image.
type Schema2RootFS struct {
	Type    string          `json:"type"`
	DiffIDs []digest.Digest `json:"diff_ids,omitempty"`
}

// Schema2History stores build commands that were used to create an image, from docker/docker/image.
type Schema2History struct {
	// Created is the timestamp at which the image was created
	Created time.Time `json:"created"`
	// Author is the name of the author that was specified when committing the image
	Author string `json:"author,omitempty"`
	// CreatedBy keeps the Dockerfile command used while building the image
	CreatedBy string `json:"created_by,omitempty"`
	// Comment is the commit message that was set when committing the image
	Comment string `json:"comment,omitempty"`
	// EmptyLayer is set to true if this history item did not generate a
	// layer. Otherwise, the history item is associated with the next
	// layer in the RootFS section.
	EmptyLayer bool `json:"empty_layer,omitempty"`
}

// Schema2Image is an Image in docker/docker/image.
type Schema2Image struct {
	Schema2V1Image
	Parent     digest.Digest    `json:"parent,omitempty"`
	RootFS     *Schema2RootFS   `json:"rootfs,omitempty"`
	History    []Schema2History `json:"history,omitempty"`
	OSVersion  string           `json:"os.version,omitempty"`
	OSFeatures []string         `json:"os.features,omitempty"`
}

// Schema2FromManifest creates a Schema2 manifest instance from a manifest blob.
func Schema2FromManifest(manifest []byte) (*Schema2, error) {
	s2 := Schema2{}
	if err := json.Unmarshal(manifest, &s2); err != nil {
		return nil, err
	}
	return &s2, nil
}

// Schema2FromComponents creates an Schema2 manifest instance from the supplied data.
func Schema2FromComponents(config Schema2Descriptor, layers []Schema2Descriptor) *Schema2 {
	return &Schema2{
		SchemaVersion:     2,
		MediaType:         DockerV2Schema2MediaType,
		ConfigDescriptor:  config,
		LayersDescriptors: layers,
	}
}

// Schema2Clone creates a copy of the supplied Schema2 manifest.
func Schema2Clone(src *Schema2) *Schema2 {
	copy := *src
	return &copy
}

// ConfigInfo returns a complete BlobInfo for the separate config object, or a BlobInfo{Digest:""} if there isn't a separate object.
func (m *Schema2) ConfigInfo() types.BlobInfo {
	return BlobInfoFromSchema2Descriptor(m.ConfigDescriptor)
}

// LayerInfos returns a list of LayerInfos of layers referenced by this image, in order (the root layer first, and then successive layered layers).
// The Digest field is guaranteed to be provided; Size may be -1.
// WARNING: The list may contain duplicates, and they are semantically relevant.
func (m *Schema2) LayerInfos() []LayerInfo {
	blobs := []LayerInfo{}
	for _, layer := range m.LayersDescriptors {
		blobs = append(blobs, LayerInfo{
			BlobInfo:   BlobInfoFromSchema2Descriptor(layer),
			EmptyLayer: false,
		})
	}
	return blobs
}

// UpdateLayerInfos replaces the original layers with the specified BlobInfos (size+digest+urls), in order (the root layer first, and then successive layered layers)
func (m *Schema2) UpdateLayerInfos(layerInfos []types.BlobInfo) error {
	if len(m.LayersDescriptors) != len(layerInfos) {
		return errors.Errorf("Error preparing updated manifest: layer count changed from %d to %d", len(m.LayersDescriptors), len(layerInfos))
	}
	original := m.LayersDescriptors
	m.LayersDescriptors = make([]Schema2Descriptor, len(layerInfos))
	for i, info := range layerInfos {
		m.LayersDescriptors[i].MediaType = original[i].MediaType
		m.LayersDescriptors[i].Digest = info.Digest
		m.LayersDescriptors[i].Size = info.Size
		m.LayersDescriptors[i].URLs = info.URLs
	}
	return nil
}

// Serialize returns the manifest in a blob format.
// NOTE: Serialize() does not in general reproduce the original blob if this object was loaded from one, even if no modifications were made!
func (m *Schema2) Serialize() ([]byte, error) {
	return json.Marshal(*m)
}

// Inspect returns various information for (skopeo inspect) parsed from the manifest and configuration.
func (m *Schema2) Inspect(configGetter func(types.BlobInfo) ([]byte, error)) (*types.ImageInspectInfo, error) {
	config, err := configGetter(m.ConfigInfo())
	if err != nil {
		return nil, err
	}
	s2 := &Schema2Image{}
	if err := json.Unmarshal(config, s2); err != nil {
		return nil, err
	}
	i := &types.ImageInspectInfo{
		Tag:           "",
		Created:       &s2.Created,
		DockerVersion: s2.DockerVersion,
		Architecture:  s2.Architecture,
		Os:            s2.OS,
		Layers:        layerInfosToStrings(m.LayerInfos()),
	}
	if s2.Config != nil {
		i.Labels = s2.Config.Labels
	}
	return i, nil
}

// ImageID computes an ID which can uniquely identify this image by its contents.
func (m *Schema2) ImageID([]digest.Digest) (string, error) {
	if err := m.ConfigDescriptor.Digest.Validate(); err != nil {
		return "", err
	}
	return m.ConfigDescriptor.Digest.Hex(), nil
}
<commit_msg>Add manifest.Schema2HealthConfig.StartPeriod<commit_after>package manifest

import (
	"encoding/json"
	"time"

	"github.com/containers/image/pkg/strslice"
	"github.com/containers/image/types"
	"github.com/opencontainers/go-digest"
	"github.com/pkg/errors"
)

// Schema2Descriptor is a “descriptor” in docker/distribution schema 2.
type Schema2Descriptor struct {
	MediaType string        `json:"mediaType"`
	Size      int64         `json:"size"`
	Digest    digest.Digest `json:"digest"`
	URLs      []string      `json:"urls,omitempty"`
}

// BlobInfoFromSchema2Descriptor returns a types.BlobInfo based on the input schema 2 descriptor.
func BlobInfoFromSchema2Descriptor(desc Schema2Descriptor) types.BlobInfo {
	return types.BlobInfo{
		Digest:    desc.Digest,
		Size:      desc.Size,
		URLs:      desc.URLs,
		MediaType: desc.MediaType,
	}
}

// Schema2 is a manifest in docker/distribution schema 2.
type Schema2 struct {
	SchemaVersion     int                 `json:"schemaVersion"`
	MediaType         string              `json:"mediaType"`
	ConfigDescriptor  Schema2Descriptor   `json:"config"`
	LayersDescriptors []Schema2Descriptor `json:"layers"`
}

// Schema2Port is a Port, a string containing port number and protocol in the
// format "80/tcp", from docker/go-connections/nat.
type Schema2Port string

// Schema2PortSet is a PortSet, a collection of structs indexed by Port, from
// docker/go-connections/nat.
type Schema2PortSet map[Schema2Port]struct{}

// Schema2HealthConfig is a HealthConfig, which holds configuration settings
// for the HEALTHCHECK feature, from docker/docker/api/types/container.
type Schema2HealthConfig struct {
	// Test is the test to perform to check that the container is healthy.
	// An empty slice means to inherit the default.
	// The options are:
	// {} : inherit healthcheck
	// {"NONE"} : disable healthcheck
	// {"CMD", args...} : exec arguments directly
	// {"CMD-SHELL", command} : run command with system's default shell
	Test []string `json:",omitempty"`

	// Zero means to inherit. Durations are expressed as integer nanoseconds.
	StartPeriod time.Duration `json:",omitempty"` // StartPeriod is the time to wait after starting before running the first check.
	Interval    time.Duration `json:",omitempty"` // Interval is the time to wait between checks.
	Timeout     time.Duration `json:",omitempty"` // Timeout is the time to wait before considering the check to have hung.

	// Retries is the number of consecutive failures needed to consider a container as unhealthy.
	// Zero means inherit.
	Retries int `json:",omitempty"`
}

// Schema2Config is a Config in docker/docker/api/types/container.
type Schema2Config struct {
	Hostname        string               // Hostname
	Domainname      string               // Domainname
	User            string               // User that will run the command(s) inside the container, also support user:group
	AttachStdin     bool                 // Attach the standard input, makes possible user interaction
	AttachStdout    bool                 // Attach the standard output
	AttachStderr    bool                 // Attach the standard error
	ExposedPorts    Schema2PortSet       `json:",omitempty"` // List of exposed ports
	Tty             bool                 // Attach standard streams to a tty, including stdin if it is not closed.
	OpenStdin       bool                 // Open stdin
	StdinOnce       bool                 // If true, close stdin after the 1 attached client disconnects.
	Env             []string             // List of environment variable to set in the container
	Cmd             strslice.StrSlice    // Command to run when starting the container
	Healthcheck     *Schema2HealthConfig `json:",omitempty"` // Healthcheck describes how to check the container is healthy
	ArgsEscaped     bool                 `json:",omitempty"` // True if command is already escaped (Windows specific)
	Image           string               // Name of the image as it was passed by the operator (e.g. could be symbolic)
	Volumes         map[string]struct{}  // List of volumes (mounts) used for the container
	WorkingDir      string               // Current directory (PWD) in the command will be launched
	Entrypoint      strslice.StrSlice    // Entrypoint to run when starting the container
	NetworkDisabled bool                 `json:",omitempty"` // Is network disabled
	MacAddress      string               `json:",omitempty"` // Mac Address of the container
	OnBuild         []string             // ONBUILD metadata that were defined on the image Dockerfile
	Labels          map[string]string    // List of labels set to this container
	StopSignal      string               `json:",omitempty"` // Signal to stop a container
	StopTimeout     *int                 `json:",omitempty"` // Timeout (in seconds) to stop a container
	Shell           strslice.StrSlice    `json:",omitempty"` // Shell for shell-form of RUN, CMD, ENTRYPOINT
}

// Schema2V1Image is a V1Image in docker/docker/image.
type Schema2V1Image struct {
	// ID is a unique 64 character identifier of the image
	ID string `json:"id,omitempty"`
	// Parent is the ID of the parent image
	Parent string `json:"parent,omitempty"`
	// Comment is the commit message that was set when committing the image
	Comment string `json:"comment,omitempty"`
	// Created is the timestamp at which the image was created
	Created time.Time `json:"created"`
	// Container is the id of the container used to commit
	Container string `json:"container,omitempty"`
	// ContainerConfig is the configuration of the container that is committed into the image
	ContainerConfig Schema2Config `json:"container_config,omitempty"`
	// DockerVersion specifies the version of Docker that was used to build the image
	DockerVersion string `json:"docker_version,omitempty"`
	// Author is the name of the author that was specified when committing the image
	Author string `json:"author,omitempty"`
	// Config is the configuration of the container received from the client
	Config *Schema2Config `json:"config,omitempty"`
	// Architecture is the hardware that the image is build and runs on
	Architecture string `json:"architecture,omitempty"`
	// OS is the operating system used to build and run the image
	OS string `json:"os,omitempty"`
	// Size is the total size of the image including all layers it is composed of
	Size int64 `json:",omitempty"`
}

// Schema2RootFS is a description of how to build up an image's root filesystem, from docker/docker/image.
type Schema2RootFS struct {
	Type    string          `json:"type"`
	DiffIDs []digest.Digest `json:"diff_ids,omitempty"`
}

// Schema2History stores build commands that were used to create an image, from docker/docker/image.
type Schema2History struct {
	// Created is the timestamp at which the image was created
	Created time.Time `json:"created"`
	// Author is the name of the author that was specified when committing the image
	Author string `json:"author,omitempty"`
	// CreatedBy keeps the Dockerfile command used while building the image
	CreatedBy string `json:"created_by,omitempty"`
	// Comment is the commit message that was set when committing the image
	Comment string `json:"comment,omitempty"`
	// EmptyLayer is set to true if this history item did not generate a
	// layer. Otherwise, the history item is associated with the next
	// layer in the RootFS section.
	EmptyLayer bool `json:"empty_layer,omitempty"`
}

// Schema2Image is an Image in docker/docker/image.
type Schema2Image struct {
	Schema2V1Image
	Parent     digest.Digest    `json:"parent,omitempty"`
	RootFS     *Schema2RootFS   `json:"rootfs,omitempty"`
	History    []Schema2History `json:"history,omitempty"`
	OSVersion  string           `json:"os.version,omitempty"`
	OSFeatures []string         `json:"os.features,omitempty"`
}

// Schema2FromManifest creates a Schema2 manifest instance from a manifest blob.
func Schema2FromManifest(manifest []byte) (*Schema2, error) {
	s2 := Schema2{}
	if err := json.Unmarshal(manifest, &s2); err != nil {
		return nil, err
	}
	return &s2, nil
}

// Schema2FromComponents creates an Schema2 manifest instance from the supplied data.
func Schema2FromComponents(config Schema2Descriptor, layers []Schema2Descriptor) *Schema2 {
	return &Schema2{
		SchemaVersion:     2,
		MediaType:         DockerV2Schema2MediaType,
		ConfigDescriptor:  config,
		LayersDescriptors: layers,
	}
}

// Schema2Clone creates a copy of the supplied Schema2 manifest.
func Schema2Clone(src *Schema2) *Schema2 {
	copy := *src
	return &copy
}

// ConfigInfo returns a complete BlobInfo for the separate config object, or a BlobInfo{Digest:""} if there isn't a separate object.
func (m *Schema2) ConfigInfo() types.BlobInfo {
	return BlobInfoFromSchema2Descriptor(m.ConfigDescriptor)
}

// LayerInfos returns a list of LayerInfos of layers referenced by this image, in order (the root layer first, and then successive layered layers).
// The Digest field is guaranteed to be provided; Size may be -1.
// WARNING: The list may contain duplicates, and they are semantically relevant.
func (m *Schema2) LayerInfos() []LayerInfo {
	blobs := []LayerInfo{}
	for _, layer := range m.LayersDescriptors {
		blobs = append(blobs, LayerInfo{
			BlobInfo:   BlobInfoFromSchema2Descriptor(layer),
			EmptyLayer: false,
		})
	}
	return blobs
}

// UpdateLayerInfos replaces the original layers with the specified BlobInfos (size+digest+urls), in order (the root layer first, and then successive layered layers)
func (m *Schema2) UpdateLayerInfos(layerInfos []types.BlobInfo) error {
	if len(m.LayersDescriptors) != len(layerInfos) {
		return errors.Errorf("Error preparing updated manifest: layer count changed from %d to %d", len(m.LayersDescriptors), len(layerInfos))
	}
	original := m.LayersDescriptors
	m.LayersDescriptors = make([]Schema2Descriptor, len(layerInfos))
	for i, info := range layerInfos {
		m.LayersDescriptors[i].MediaType = original[i].MediaType
		m.LayersDescriptors[i].Digest = info.Digest
		m.LayersDescriptors[i].Size = info.Size
		m.LayersDescriptors[i].URLs = info.URLs
	}
	return nil
}

// Serialize returns the manifest in a blob format.
// NOTE: Serialize() does not in general reproduce the original blob if this object was loaded from one, even if no modifications were made!
func (m *Schema2) Serialize() ([]byte, error) {
	return json.Marshal(*m)
}

// Inspect returns various information for (skopeo inspect) parsed from the manifest and configuration.
func (m *Schema2) Inspect(configGetter func(types.BlobInfo) ([]byte, error)) (*types.ImageInspectInfo, error) {
	config, err := configGetter(m.ConfigInfo())
	if err != nil {
		return nil, err
	}
	s2 := &Schema2Image{}
	if err := json.Unmarshal(config, s2); err != nil {
		return nil, err
	}
	i := &types.ImageInspectInfo{
		Tag:           "",
		Created:       &s2.Created,
		DockerVersion: s2.DockerVersion,
		Architecture:  s2.Architecture,
		Os:            s2.OS,
		Layers:        layerInfosToStrings(m.LayerInfos()),
	}
	if s2.Config != nil {
		i.Labels = s2.Config.Labels
	}
	return i, nil
}

// ImageID computes an ID which can uniquely identify this image by its contents.
func (m *Schema2) ImageID([]digest.Digest) (string, error) {
	if err := m.ConfigDescriptor.Digest.Validate(); err != nil {
		return "", err
	}
	return m.ConfigDescriptor.Digest.Hex(), nil
}
<|endoftext|>


<commit_before>package main

import (
	"testing"
)

func TestGithubMarkdownConverter(t *testing.T) {
	gmc := NewGithubMarkdownConverter()

	html, err := gmc.Convert("Hello World")

	if err != nil {
		t.Fatalf("Unable to convert markdown : %s", err.Error())
	}

	expect := "
Hello World
"
	if html != expect {
		t.Fatalf("Expect html is %s, but got %q ", expect, html)
	}

}
<commit_msg>fix broken test<commit_after>package main

import (
	"testing"
)

func TestGithubMarkdownConverter(t *testing.T) {
	gmc := NewGithubMarkdownConverter()

	html, err := gmc.Convert("Hello World")

	if err != nil {
		t.Fatalf("Unable to convert markdown : %s", err.Error())
	}

	expect := "
Hello World
\n"
	if html != expect {
		t.Fatalf("Expect html is %s, but got %q ", expect, html)
	}

}
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"errors"
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"path"
	"sync"
	"syscall"
	"time"
)

import "git.torproject.org/pluggable-transports/goptlib.git"

const (
	ptMethodName       = "meek"
	minSessionIdLength = 32
	maxPayloadLength   = 0x10000
	// How long we try to read something back from the ORPort before returning the
	// response.
	turnaroundTimeout = 10 * time.Millisecond
	// Passed as ReadTimeout and WriteTimeout when constructing the http.Server.
	readWriteTimeout    = 20 * time.Second
	maxSessionStaleness = 120 * time.Second
)

var ptInfo pt.ServerInfo

// When a connection handler starts, +1 is written to this channel; when it
// ends, -1 is written.
var handlerChan = make(chan int)

func httpBadRequest(w http.ResponseWriter) {
	http.Error(w, "Bad request.\n", http.StatusBadRequest)
}

func httpInternalServerError(w http.ResponseWriter) {
	http.Error(w, "Internal server error.\n", http.StatusInternalServerError)
}

type Session struct {
	Or       *net.TCPConn
	LastSeen time.Time
}

func (session *Session) Touch() {
	session.LastSeen = time.Now()
}

func (session *Session) Expired() bool {
	return time.Since(session.LastSeen) > maxSessionStaleness
}

type State struct {
	sessionMap map[string]*Session
	lock       sync.Mutex
}

func NewState() *State {
	state := new(State)
	state.sessionMap = make(map[string]*Session)
	return state
}

func (state *State) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	handlerChan <- 1
	defer func() {
		handlerChan <- -1
	}()

	switch req.Method {
	case "GET":
		state.Get(w, req)
	case "POST":
		state.Post(w, req)
	default:
		httpBadRequest(w)
	}
}

func (state *State) Get(w http.ResponseWriter, req *http.Request) {
	if path.Clean(req.URL.Path) != "/" {
		http.NotFound(w, req)
		return
	}
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("I’m just a happy little web server.\n"))
}

func (state *State) GetSession(sessionId string, req *http.Request) (*Session, error) {
	state.lock.Lock()
	defer state.lock.Unlock()

	session := state.sessionMap[sessionId]
	if session == nil {
		// log.Printf("unknown session id %q; creating new session", sessionId)

		or, err := pt.DialOr(&ptInfo, req.RemoteAddr, ptMethodName)
		if err != nil {
			return nil, err
		}
		session = &Session{Or: or}
		state.sessionMap[sessionId] = session
	}
	session.Touch()

	return session, nil
}

func transact(session *Session, w http.ResponseWriter, req *http.Request) error {
	body := http.MaxBytesReader(w, req.Body, maxPayloadLength+1)
	_, err := io.Copy(session.Or, body)
	if err != nil {
		return errors.New(fmt.Sprintf("copying body to ORPort: %s", err))
	}

	buf := make([]byte, maxPayloadLength)
	session.Or.SetReadDeadline(time.Now().Add(turnaroundTimeout))
	n, err := session.Or.Read(buf)
	if err != nil {
		if e, ok := err.(net.Error); !ok || !e.Timeout() {
			httpInternalServerError(w)
			return errors.New(fmt.Sprintf("reading from ORPort: %s", err))
		}
	}
	// log.Printf("read %d bytes from ORPort: %q", n, buf[:n])
	n, err = w.Write(buf[:n])
	if err != nil {
		return errors.New(fmt.Sprintf("writing to response: %s", err))
	}
	// log.Printf("wrote %d bytes to response", n)
	return nil
}

func (state *State) Post(w http.ResponseWriter, req *http.Request) {
	sessionId := req.Header.Get("X-Session-Id")
	if len(sessionId) < minSessionIdLength {
		httpBadRequest(w)
		return
	}

	session, err := state.GetSession(sessionId, req)
	if err != nil {
		log.Print(err)
		httpInternalServerError(w)
		return
	}

	err = transact(session, w, req)
	if err != nil {
		log.Print(err)
		state.CloseSession(sessionId)
		return
	}
}

func (state *State) CloseSession(sessionId string) {
	state.lock.Lock()
	defer state.lock.Unlock()
	// log.Printf("closing session %q", sessionId)
	session, ok := state.sessionMap[sessionId]
	if ok {
		session.Or.Close()
		delete(state.sessionMap, sessionId)
	}
}

func (state *State) ExpireSessions() {
	for {
		time.Sleep(maxSessionStaleness / 2)
		state.lock.Lock()
		for sessionId, session := range state.sessionMap {
			if session.Expired() {
				// log.Printf("deleting expired session %q", sessionId)
				session.Or.Close()
				delete(state.sessionMap, sessionId)
			}
		}
		state.lock.Unlock()
	}
}

func listenTLS(network string, addr *net.TCPAddr, certFilename, keyFilename string) (net.Listener, error) {
	// This is cribbed from the source of net/http.Server.ListenAndServeTLS.
	// We have to separate the Listen and Serve parts because we need to
	// report the listening address before entering Serve (which is an
	// infinite loop).
	// https://groups.google.com/d/msg/Golang-nuts/3F1VRCCENp8/3hcayZiwYM8J
	config := &tls.Config{}
	config.NextProtos = []string{"http/1.1"}

	var err error
	config.Certificates = make([]tls.Certificate, 1)
	config.Certificates[0], err = tls.LoadX509KeyPair(certFilename, keyFilename)
	if err != nil {
		return nil, err
	}

	conn, err := net.ListenTCP(network, addr)
	if err != nil {
		return nil, err
	}

	tlsListener := tls.NewListener(conn, config)

	return tlsListener, nil
}

func startListener(network string, addr *net.TCPAddr) (net.Listener, error) {
	ln, err := net.ListenTCP(network, addr)
	if err != nil {
		return nil, err
	}
	log.Printf("listening with plain HTTP on %s", ln.Addr())
	return startServer(ln)
}

func startListenerTLS(network string, addr *net.TCPAddr, certFilename, keyFilename string) (net.Listener, error) {
	ln, err := listenTLS(network, addr, certFilename, keyFilename)
	if err != nil {
		return nil, err
	}
	log.Printf("listening with HTTPS on %s", ln.Addr())
	return startServer(ln)
}

func startServer(ln net.Listener) (net.Listener, error) {
	state := NewState()
	go state.ExpireSessions()
	server := &http.Server{
		Handler:      state,
		ReadTimeout:  readWriteTimeout,
		WriteTimeout: readWriteTimeout,
	}
	go func() {
		defer ln.Close()
		err := server.Serve(ln)
		if err != nil {
			log.Printf("Error in Serve: %s", err)
		}
	}()
	return ln, nil
}

func main() {
	var disableTLS bool
	var certFilename, keyFilename string
	var logFilename string
	var port int

	flag.BoolVar(&disableTLS, "disable-tls", false, "don't use HTTPS")
	flag.StringVar(&certFilename, "cert", "", "TLS certificate file (required without --disable-tls)")
	flag.StringVar(&keyFilename, "key", "", "TLS private key file (required without --disable-tls)")
	flag.StringVar(&logFilename, "log", "", "name of log file")
	flag.IntVar(&port, "port", 0, "port to listen on")
	flag.Parse()

	if logFilename != "" {
		f, err := os.OpenFile(logFilename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
		if err != nil {
			log.Fatalf("error opening log file: %s", err)
		}
		defer f.Close()
		log.SetOutput(f)
	}

	if disableTLS {
		if certFilename != "" || keyFilename != "" {
			log.Fatalf("The --cert and --key options are not allowed with --disable-tls.\n")
		}
	} else {
		if certFilename == "" || keyFilename == "" {
			log.Fatalf("The --cert and --key options are required.\n")
		}
	}

	var err error
	ptInfo, err = pt.ServerSetup([]string{ptMethodName})
	if err != nil {
		log.Fatalf("error in ServerSetup: %s", err)
	}

	log.Printf("starting")
	listeners := make([]net.Listener, 0)
	for _, bindaddr := range ptInfo.Bindaddrs {
		if port != 0 {
			bindaddr.Addr.Port = port
		}
		switch bindaddr.MethodName {
		case ptMethodName:
			var ln net.Listener
			if disableTLS {
				ln, err = startListener("tcp", bindaddr.Addr)
			} else {
				ln, err = startListenerTLS("tcp", bindaddr.Addr, certFilename, keyFilename)
			}
			if err != nil {
				pt.SmethodError(bindaddr.MethodName, err.Error())
				break
			}
			pt.Smethod(bindaddr.MethodName, ln.Addr())
			listeners = append(listeners, ln)
		default:
			pt.SmethodError(bindaddr.MethodName, "no such method")
		}
	}
	pt.SmethodsDone()

	var numHandlers int = 0
	var sig os.Signal
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

	// wait for first signal
	sig = nil
	for sig == nil {
		select {
		case n := <-handlerChan:
			numHandlers += n
		case sig = <-sigChan:
		}
	}
	for _, ln := range listeners {
		ln.Close()
	}

	if sig == syscall.SIGTERM {
		return
	}

	// wait for second signal or no more handlers
	sig = nil
	for sig == nil && numHandlers != 0 {
		select {
		case n := <-handlerChan:
			numHandlers += n
		case sig = <-sigChan:
		}
	}

	log.Printf("done")
}
<commit_msg>Rename Expired to IsExpired.<commit_after>package main

import (
	"crypto/tls"
	"errors"
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"path"
	"sync"
	"syscall"
	"time"
)

import "git.torproject.org/pluggable-transports/goptlib.git"

const (
	ptMethodName       = "meek"
	minSessionIdLength = 32
	maxPayloadLength   = 0x10000
	// How long we try to read something back from the ORPort before returning the
	// response.
	turnaroundTimeout = 10 * time.Millisecond
	// Passed as ReadTimeout and WriteTimeout when constructing the http.Server.
	readWriteTimeout    = 20 * time.Second
	maxSessionStaleness = 120 * time.Second
)

var ptInfo pt.ServerInfo

// When a connection handler starts, +1 is written to this channel; when it
// ends, -1 is written.
var handlerChan = make(chan int)

func httpBadRequest(w http.ResponseWriter) {
	http.Error(w, "Bad request.\n", http.StatusBadRequest)
}

func httpInternalServerError(w http.ResponseWriter) {
	http.Error(w, "Internal server error.\n", http.StatusInternalServerError)
}

type Session struct {
	Or       *net.TCPConn
	LastSeen time.Time
}

func (session *Session) Touch() {
	session.LastSeen = time.Now()
}

func (session *Session) IsExpired() bool {
	return time.Since(session.LastSeen) > maxSessionStaleness
}

type State struct {
	sessionMap map[string]*Session
	lock       sync.Mutex
}

func NewState() *State {
	state := new(State)
	state.sessionMap = make(map[string]*Session)
	return state
}

func (state *State) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	handlerChan <- 1
	defer func() {
		handlerChan <- -1
	}()

	switch req.Method {
	case "GET":
		state.Get(w, req)
	case "POST":
		state.Post(w, req)
	default:
		httpBadRequest(w)
	}
}

func (state *State) Get(w http.ResponseWriter, req *http.Request) {
	if path.Clean(req.URL.Path) != "/" {
		http.NotFound(w, req)
		return
	}
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("I’m just a happy little web server.\n"))
}

func (state *State) GetSession(sessionId string, req *http.Request) (*Session, error) {
	state.lock.Lock()
	defer state.lock.Unlock()

	session := state.sessionMap[sessionId]
	if session == nil {
		// log.Printf("unknown session id %q; creating new session", sessionId)

		or, err := pt.DialOr(&ptInfo, req.RemoteAddr, ptMethodName)
		if err != nil {
			return nil, err
		}
		session = &Session{Or: or}
		state.sessionMap[sessionId] = session
	}
	session.Touch()

	return session, nil
}

func transact(session *Session, w http.ResponseWriter, req *http.Request) error {
	body := http.MaxBytesReader(w, req.Body, maxPayloadLength+1)
	_, err := io.Copy(session.Or, body)
	if err != nil {
		return errors.New(fmt.Sprintf("copying body to ORPort: %s", err))
	}

	buf := make([]byte, maxPayloadLength)
	session.Or.SetReadDeadline(time.Now().Add(turnaroundTimeout))
	n, err := session.Or.Read(buf)
	if err != nil {
		if e, ok := err.(net.Error); !ok || !e.Timeout() {
			httpInternalServerError(w)
			return errors.New(fmt.Sprintf("reading from ORPort: %s", err))
		}
	}
	// log.Printf("read %d bytes from ORPort: %q", n, buf[:n])
	n, err = w.Write(buf[:n])
	if err != nil {
		return errors.New(fmt.Sprintf("writing to response: %s", err))
	}
	// log.Printf("wrote %d bytes to response", n)
	return nil
}

func (state *State) Post(w http.ResponseWriter, req *http.Request) {
	sessionId := req.Header.Get("X-Session-Id")
	if len(sessionId) < minSessionIdLength {
		httpBadRequest(w)
		return
	}

	session, err := state.GetSession(sessionId, req)
	if err != nil {
		log.Print(err)
		httpInternalServerError(w)
		return
	}

	err = transact(session, w, req)
	if err != nil {
		log.Print(err)
		state.CloseSession(sessionId)
		return
	}
}

func (state *State) CloseSession(sessionId string) {
	state.lock.Lock()
	defer state.lock.Unlock()
	// log.Printf("closing session %q", sessionId)
	session, ok := state.sessionMap[sessionId]
	if ok {
		session.Or.Close()
		delete(state.sessionMap, sessionId)
	}
}

func (state *State) ExpireSessions() {
	for {
		time.Sleep(maxSessionStaleness / 2)
		state.lock.Lock()
		for sessionId, session := range state.sessionMap {
			if session.IsExpired() {
				// log.Printf("deleting expired session %q", sessionId)
				session.Or.Close()
				delete(state.sessionMap, sessionId)
			}
		}
		state.lock.Unlock()
	}
}

func listenTLS(network string, addr *net.TCPAddr, certFilename, keyFilename string) (net.Listener, error) {
	// This is cribbed from the source of net/http.Server.ListenAndServeTLS.
	// We have to separate the Listen and Serve parts because we need to
	// report the listening address before entering Serve (which is an
	// infinite loop).
	// https://groups.google.com/d/msg/Golang-nuts/3F1VRCCENp8/3hcayZiwYM8J
	config := &tls.Config{}
	config.NextProtos = []string{"http/1.1"}

	var err error
	config.Certificates = make([]tls.Certificate, 1)
	config.Certificates[0], err = tls.LoadX509KeyPair(certFilename, keyFilename)
	if err != nil {
		return nil, err
	}

	conn, err := net.ListenTCP(network, addr)
	if err != nil {
		return nil, err
	}

	tlsListener := tls.NewListener(conn, config)

	return tlsListener, nil
}

func startListener(network string, addr *net.TCPAddr) (net.Listener, error) {
	ln, err := net.ListenTCP(network, addr)
	if err != nil {
		return nil, err
	}
	log.Printf("listening with plain HTTP on %s", ln.Addr())
	return startServer(ln)
}

func startListenerTLS(network string, addr *net.TCPAddr, certFilename, keyFilename string) (net.Listener, error) {
	ln, err := listenTLS(network, addr, certFilename, keyFilename)
	if err != nil {
		return nil, err
	}
	log.Printf("listening with HTTPS on %s", ln.Addr())
	return startServer(ln)
}

func startServer(ln net.Listener) (net.Listener, error) {
	state := NewState()
	go state.ExpireSessions()
	server := &http.Server{
		Handler:      state,
		ReadTimeout:  readWriteTimeout,
		WriteTimeout: readWriteTimeout,
	}
	go func() {
		defer ln.Close()
		err := server.Serve(ln)
		if err != nil {
			log.Printf("Error in Serve: %s", err)
		}
	}()
	return ln, nil
}

func main() {
	var disableTLS bool
	var certFilename, keyFilename string
	var logFilename string
	var port int

	flag.BoolVar(&disableTLS, "disable-tls", false, "don't use HTTPS")
	flag.StringVar(&certFilename, "cert", "", "TLS certificate file (required without --disable-tls)")
	flag.StringVar(&keyFilename, "key", "", "TLS private key file (required without --disable-tls)")
	flag.StringVar(&logFilename, "log", "", "name of log file")
	flag.IntVar(&port, "port", 0, "port to listen on")
	flag.Parse()

	if logFilename != "" {
		f, err := os.OpenFile(logFilename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
		if err != nil {
			log.Fatalf("error opening log file: %s", err)
		}
		defer f.Close()
		log.SetOutput(f)
	}

	if disableTLS {
		if certFilename != "" || keyFilename != "" {
			log.Fatalf("The --cert and --key options are not allowed with --disable-tls.\n")
		}
	} else {
		if certFilename == "" || keyFilename == "" {
			log.Fatalf("The --cert and --key options are required.\n")
		}
	}

	var err error
	ptInfo, err = pt.ServerSetup([]string{ptMethodName})
	if err != nil {
		log.Fatalf("error in ServerSetup: %s", err)
	}

	log.Printf("starting")
	listeners := make([]net.Listener, 0)
	for _, bindaddr := range ptInfo.Bindaddrs {
		if port != 0 {
			bindaddr.Addr.Port = port
		}
		switch bindaddr.MethodName {
		case ptMethodName:
			var ln net.Listener
			if disableTLS {
				ln, err = startListener("tcp", bindaddr.Addr)
			} else {
				ln, err = startListenerTLS("tcp", bindaddr.Addr, certFilename, keyFilename)
			}
			if err != nil {
				pt.SmethodError(bindaddr.MethodName, err.Error())
				break
			}
			pt.Smethod(bindaddr.MethodName, ln.Addr())
			listeners = append(listeners, ln)
		default:
			pt.SmethodError(bindaddr.MethodName, "no such method")
		}
	}
	pt.SmethodsDone()

	var numHandlers int = 0
	var sig os.Signal
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

	// wait for first signal
	sig = nil
	for sig == nil {
		select {
		case n := <-handlerChan:
			numHandlers += n
		case sig = <-sigChan:
		}
	}
	for _, ln := range listeners {
		ln.Close()
	}

	if sig == syscall.SIGTERM {
		return
	}

	// wait for second signal or no more handlers
	sig = nil
	for sig == nil && numHandlers != 0 {
		select {
		case n := <-handlerChan:
			numHandlers += n
		case sig = <-sigChan:
		}
	}

	log.Printf("done")
}
<|endoftext|>


<commit_before>package backend

import (
	"errors"
	"fmt"
	"sync"
)

// BackendCreator is a function that created a specific type of backend from config.
type BackendCreator func(args interface{}) (Backend, error)

var block sync.Mutex
var backends map[string]BackendCreator

// Register backend creator by backend type.
func RegisterCreator(btype string, creator BackendCreator) error {
	block.Lock()
	defer block.Unlock()

	if backends == nil {
		backends = map[string]BackendCreator{}
	}

	_, ok := backends[btype]
	if ok {
		return errors.New(fmt.Sprintf("RegisterCreator: there already is backend creator for type \"%s\"", btype))
	}

	backends[btype] = creator
	return nil
}

// Create a backend by it's type and config.
func Create(btype string, args interface{}) (Backend, error) {
	block.Lock()
	defer block.Unlock()

	r, ok := backends[btype]
	if !ok {
		return nil, errors.New(fmt.Sprintf("Create: no backend type \"%s\"", btype))
	}
	return r(args)
}

// List all the backend types registered.
func CreatorTypes() []string {
	block.Lock()
	defer block.Unlock()

	res := make([]string, 0, len(backends))
	for k, _ := range backends {
		res = append(res, k)
	}
	return res
}

// Register backend creators provided by this library.
func RegisterDefault() {
	RegisterCreator("nil", func(arg interface{}) (Backend, error) {
		return NewNil(), nil
	})
	RegisterCreator("mem", func(arg interface{}) (Backend, error) {
		return NewMem(), nil
	})
	RegisterCreator("ledis", func(arg interface{}) (Backend, error) {
		dir, ok := arg.(string)
		if !ok {
			return nil, errors.New(fmt.Sprintf("ledis creator: Expected *string as arg, got %v", arg))
		}
		return NewLedis(dir)
	})
	RegisterCreator("http", func(arg interface{}) (Backend, error) {
		url, ok := arg.(string)
		if !ok {
			return nil, errors.New(fmt.Sprintf("http-default creator: Expected *string as arg, got %v", arg))
		}
		return NewHttp(url, nil), nil
	})
}
<commit_msg>fixed error messages<commit_after>package backend

import (
	"errors"
	"fmt"
	"sync"
)

// BackendCreator is a function that created a specific type of backend from config.
type BackendCreator func(args interface{}) (Backend, error)

var block sync.Mutex
var backends map[string]BackendCreator

// Register backend creator by backend type.
func RegisterCreator(btype string, creator BackendCreator) error {
	block.Lock()
	defer block.Unlock()

	if backends == nil {
		backends = map[string]BackendCreator{}
	}

	_, ok := backends[btype]
	if ok {
		return errors.New(fmt.Sprintf("RegisterCreator: there already is backend creator for type \"%s\"", btype))
	}

	backends[btype] = creator
	return nil
}

// Create a backend by it's type and config.
func Create(btype string, args interface{}) (Backend, error) {
	block.Lock()
	defer block.Unlock()

	r, ok := backends[btype]
	if !ok {
		return nil, errors.New(fmt.Sprintf("Create: no backend type \"%s\"", btype))
	}
	return r(args)
}

// List all the backend types registered.
func CreatorTypes() []string {
	block.Lock()
	defer block.Unlock()

	res := make([]string, 0, len(backends))
	for k, _ := range backends {
		res = append(res, k)
	}
	return res
}

// Register backend creators provided by this library.
func RegisterDefault() {
	RegisterCreator("nil", func(arg interface{}) (Backend, error) {
		return NewNil(), nil
	})
	RegisterCreator("mem", func(arg interface{}) (Backend, error) {
		return NewMem(), nil
	})
	RegisterCreator("ledis", func(arg interface{}) (Backend, error) {
		dir, ok := arg.(string)
		if !ok {
			return nil, errors.New(fmt.Sprintf("ledis creator: Expected string as arg, got %v", arg))
		}
		return NewLedis(dir)
	})
	RegisterCreator("http", func(arg interface{}) (Backend, error) {
		url, ok := arg.(string)
		if !ok {
			return nil, errors.New(fmt.Sprintf("http-default creator: Expected string as arg, got %v", arg))
		}
		return NewHttp(url, nil), nil
	})
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/DATA-DOG/godog/gherkin"
	"github.com/sandstorm/dokku-enterprise-plugin/core/utility"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

// Create a quite-minimal dokku Dockerfile application (as basis for testing)
// which just delivers a static file.
func iHaveAnEmptyDockerfileApplication() error {
	os.RemoveAll("/tmp/bdd-test-app")
	os.Mkdir("/tmp/bdd-test-app", 0755)
	os.Chdir("/tmp/bdd-test-app")

	dockerfileContents := `
		FROM nginx:stable-alpine
		ADD . /app
		EXPOSE 5000
		RUN cp /app/nginx.conf /etc/nginx/nginx.conf
		CMD ["nginx", "-g", "daemon off;"]
	`
	ioutil.WriteFile("Dockerfile", []byte(dockerfileContents), 0644)

	checksContents := `
		WAIT=1
		http://test/ welcome
	`
	ioutil.WriteFile("CHECKS", []byte(checksContents), 0644)

	nginxConf := `
		events {
			worker_connections  1024;
		}
		http {
			server {
				listen 5000;
				listen 80;
				listen 443;
				location / {
					root   /app;
					index  index.html index.htm;
				}
			}
		}
	`
	ioutil.WriteFile("nginx.conf", []byte(nginxConf), 0644)

	index := "welcome"
	ioutil.WriteFile("index.html", []byte(index), 0644)

	return nil
}

func iCreateTheFileWithTheFollowingContents(filePath string, contents *gherkin.DocString) error {
	os.MkdirAll(filepath.Dir(filePath), 0755)
	ioutil.WriteFile(filePath, []byte(contents.Content), 0644)

	return nil
}

func iRemoveTheFile(filePath string) error {
	os.Remove(filePath)
	return nil
}

func iDeployTheApplicationAs(applicationName string) error {
	exec.Command("git", "init").Run()
	exec.Command("git", "add", ".").Run()
	exec.Command("git", "commit", "-m", "Initial commit").Run()
	result := utility.ExecCommand("git", "push", "--force", "dokku@dokku.me:"+applicationName, "HEAD:master")

	if strings.Contains(result, "Application deployed:") {
		return nil
	} else {
		return fmt.Errorf("Deployment was not successful. Full log: %s", result)
	}
}

// Call dokku with some arguments
func iCallDokku(dokkuArguments string) error {
	args := strings.Split(dokkuArguments, " ")

	utility.ExecCommand(append([]string{"ssh", "dokku@dokku.me"}, args...)...)

	return nil
}

// the HTTP response body as string; filled as result of iCallTheURLOfTheApplication()
var httpResponseBodyAfterCallingApplicationUrl string

// Call the URL of an application.
func iCallTheURLOfTheApplication(urlPath, applicationName string) error {

	// First, we need to figure out the port the application is running on; and we need to prefix "dokku.me"
	// for the host to work properly across Vagrant development environments.
	domainName := utility.ExecCommand("ssh", "dokku@dokku.me", "urls", applicationName)
	parsedUrl, _ := url.Parse(domainName)
	splitHost := strings.Split(parsedUrl.Host, ":")
	parsedUrl.Host = "dokku.me:" + splitHost[1]
	parsedUrl.Path = urlPath

	// Do the actual request, and parse the response
	result, err := http.Get(parsedUrl.String())

	if err != nil {
		return fmt.Errorf("Error while calling %s: %s", parsedUrl.String(), err)
	}

	defer result.Body.Close()
	bodyBytes, _ := ioutil.ReadAll(result.Body)
	httpResponseBodyAfterCallingApplicationUrl = string(bodyBytes[:])

	return nil
}

func theResponseShouldContain(substring string) error {
	if !strings.Contains(httpResponseBodyAfterCallingApplicationUrl, substring) {
		return fmt.Errorf("String '%s' should contain '%s', but did not.", httpResponseBodyAfterCallingApplicationUrl, substring)
	}
	return nil
}

func theResponseShouldNotContain(substring string) error {
	if strings.Contains(httpResponseBodyAfterCallingApplicationUrl, substring) {
		return fmt.Errorf("String '%s' should not contain '%s', but did.", httpResponseBodyAfterCallingApplicationUrl, substring)
	}
	return nil
}
<commit_msg>BUGFIX: fix testcases<commit_after>package main

import (
	"fmt"
	"github.com/DATA-DOG/godog/gherkin"
	"github.com/sandstorm/dokku-enterprise-plugin/core/utility"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

// Create a quite-minimal dokku Dockerfile application (as basis for testing)
// which just delivers a static file.
func iHaveAnEmptyDockerfileApplication() error {
	os.RemoveAll("/tmp/bdd-test-app")
	os.Mkdir("/tmp/bdd-test-app", 0755)
	os.Chdir("/tmp/bdd-test-app")

	dockerfileContents := `
		FROM nginx:stable-alpine
		ADD . /app
		EXPOSE 5000
		RUN cp /app/nginx.conf /etc/nginx/nginx.conf
		CMD ["nginx", "-g", "daemon off;"]
	`
	ioutil.WriteFile("Dockerfile", []byte(dockerfileContents), 0644)

	checksContents := `
		WAIT=1
		http://test/ welcome
	`
	ioutil.WriteFile("CHECKS", []byte(checksContents), 0644)

	nginxConf := `
		events {
			worker_connections  1024;
		}
		http {
			server {
				listen 5000;
				listen 80;
				listen 443;
				location / {
					root   /app;
					index  index.html index.htm;
				}
			}
		}
	`
	ioutil.WriteFile("nginx.conf", []byte(nginxConf), 0644)

	index := "welcome"
	ioutil.WriteFile("index.html", []byte(index), 0644)

	return nil
}

func iCreateTheFileWithTheFollowingContents(filePath string, contents *gherkin.DocString) error {
	os.MkdirAll(filepath.Dir(filePath), 0755)
	ioutil.WriteFile(filePath, []byte(contents.Content), 0644)

	return nil
}

func iRemoveTheFile(filePath string) error {
	os.Remove(filePath)
	return nil
}

func iDeployTheApplicationAs(applicationName string) error {
	exec.Command("git", "init").Run()
	exec.Command("git", "add", ".").Run()
	exec.Command("git", "commit", "-m", "Initial commit").Run()
	result := utility.ExecCommand("git", "push", "--force", "dokku@dokku.me:"+applicationName, "HEAD:master")

	if strings.Contains(result, "Application deployed:") {
		return nil
	} else {
		return fmt.Errorf("Deployment was not successful. Full log: %s", result)
	}
}

// Call dokku with some arguments
func iCallDokku(dokkuArguments string) error {
	args := strings.Split(dokkuArguments, " ")

	utility.ExecCommand(append([]string{"ssh", "dokku@dokku.me"}, args...)...)

	return nil
}

// the HTTP response body as string; filled as result of iCallTheURLOfTheApplication()
var httpResponseBodyAfterCallingApplicationUrl string

// Call the URL of an application.
func iCallTheURLOfTheApplication(urlPath, applicationName string) error {

	// First, we need to figure out the port the application is running on; and we need to prefix "dokku.me"
	// for the host to work properly across Vagrant development environments.

	domainNames := utility.ExecCommand("ssh", "dokku@dokku.me", "urls", applicationName)
	domainNamesArray := strings.Split(domainNames, "\n")
	parsedUrl, _ := url.Parse(domainNamesArray[0])
	splitHost := strings.Split(parsedUrl.Host, ":")
	parsedUrl.Host = "dokku.me:" + splitHost[1]
	parsedUrl.Path = urlPath

	// Do the actual request, and parse the response
	result, err := http.Get(parsedUrl.String())

	if err != nil {
		return fmt.Errorf("Error while calling %s: %s", parsedUrl.String(), err)
	}

	defer result.Body.Close()
	bodyBytes, _ := ioutil.ReadAll(result.Body)
	httpResponseBodyAfterCallingApplicationUrl = string(bodyBytes[:])

	return nil
}

func theResponseShouldContain(substring string) error {
	if !strings.Contains(httpResponseBodyAfterCallingApplicationUrl, substring) {
		return fmt.Errorf("String '%s' should contain '%s', but did not.", httpResponseBodyAfterCallingApplicationUrl, substring)
	}
	return nil
}

func theResponseShouldNotContain(substring string) error {
	if strings.Contains(httpResponseBodyAfterCallingApplicationUrl, substring) {
		return fmt.Errorf("String '%s' should not contain '%s', but did.", httpResponseBodyAfterCallingApplicationUrl, substring)
	}
	return nil
}
<|endoftext|>