<commit_before>package bugsnag

import (
	"context"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"sync"
	"time"

	"github.com/bugsnag/bugsnag-go/errors"
	"github.com/bugsnag/bugsnag-go/sessions"

	// Fixes a bug with SHA-384 intermediate certs on some platforms.
	// - https://github.com/bugsnag/bugsnag-go/issues/9
	_ "crypto/sha512"
)

// The current version of bugsnag-go.
const VERSION = "1.3.1"
const configuredMultipleTimes = "WARNING: Bugsnag was configured twice. It is recommended to only call bugsnag.Configure once to ensure consistent session tracking behavior"

var once sync.Once
var middleware middlewareStack

// The configuration for the default bugsnag notifier.
var Config Configuration
var sessionTrackingConfig sessions.SessionTrackingConfiguration

// DefaultSessionPublishInterval defines how often sessions should be sent to
// Bugsnag.
// Deprecated: Exposed for developer sanity in testing. Modify at own risk.
var DefaultSessionPublishInterval = 60 * time.Second
var defaultNotifier = Notifier{&Config, nil}
var sessionTracker sessions.SessionTracker

// Configure Bugsnag. The only required setting is the APIKey, which can be
// obtained by clicking on "Settings" in your Bugsnag dashboard. This function
// is also responsible for installing the global panic handler, so it should be
// called as early as possible in your initialization process.
func Configure(config Configuration) {
	Config.update(&config)
	once.Do(Config.PanicHandler)
	startSessionTracking()
}

// StartSession creates a clone of the context.Context instance with Bugsnag
// session data attached.
func StartSession(ctx context.Context) context.Context {
	return sessionTracker.StartSession(ctx)
}

// Notify sends an error to Bugsnag along with the current stack trace. The
// rawData is used to send extra information along with the error. For example
// you can pass the current http.Request to Bugsnag to see information about it
// in the dashboard, or set the severity of the notification.
func Notify(err error, rawData ...interface{}) error {
	return defaultNotifier.Notify(errors.New(err, 1), rawData...)
}

// AutoNotify logs a panic on a goroutine and then repanics.
// It should only be used in places that have existing panic handlers further
// up the stack. The rawData is used to send extra information along with any
// panics that are handled this way.
// Usage:
//  go func() {
//		defer bugsnag.AutoNotify()
//      // (possibly crashy code)
//  }()
// See also: bugsnag.Recover()
func AutoNotify(rawData ...interface{}) {
	if err := recover(); err != nil {
		severity := defaultNotifier.getDefaultSeverity(rawData, SeverityError)
		state := HandledState{SeverityReasonHandledPanic, severity, true, ""}
		rawData = append([]interface{}{state}, rawData...)
		defaultNotifier.NotifySync(errors.New(err, 2), true, rawData...)
		panic(err)
	}
}

// Recover logs a panic on a goroutine and then recovers.
// The rawData is used to send extra information along with
// any panics that are handled this way
// Usage: defer bugsnag.Recover()
func Recover(rawData ...interface{}) {
	if err := recover(); err != nil {
		severity := defaultNotifier.getDefaultSeverity(rawData, SeverityWarning)
		state := HandledState{SeverityReasonHandledPanic, severity, false, ""}
		rawData = append([]interface{}{state}, rawData...)
		defaultNotifier.Notify(errors.New(err, 2), rawData...)
	}
}

// OnBeforeNotify adds a callback to be run before a notification is sent to
// Bugsnag.  It can be used to modify the event or its MetaData. Changes made
// to the configuration are local to notifying about this event. To prevent the
// event from being sent to Bugsnag return an error, this error will be
// returned from bugsnag.Notify() and the event will not be sent.
func OnBeforeNotify(callback func(event *Event, config *Configuration) error) {
	middleware.OnBeforeNotify(callback)
}

// Handler creates an http Handler that notifies Bugsnag any panics that
// happen. It then repanics so that the default http Server panic handler can
// handle the panic too. The rawData is used to send extra information along
// with any panics that are handled this way.
func Handler(h http.Handler, rawData ...interface{}) http.Handler {
	notifier := New(rawData...)
	if h == nil {
		h = http.DefaultServeMux
	}

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		defer notifier.AutoNotify(r)
		h.ServeHTTP(w, r)
	})
}

// HandlerFunc creates an http HandlerFunc that notifies Bugsnag about any
// panics that happen. It then repanics so that the default http Server panic
// handler can handle the panic too. The rawData is used to send extra
// information along with any panics that are handled this way. If you have
// already wrapped your http server using bugsnag.Handler() you don't also need
// to wrap each HandlerFunc.
func HandlerFunc(h http.HandlerFunc, rawData ...interface{}) http.HandlerFunc {
	notifier := New(rawData...)

	return func(w http.ResponseWriter, r *http.Request) {
		defer notifier.AutoNotify(r)
		h(w, r)
	}
}

func init() {
	// Set up builtin middlewarez
	OnBeforeNotify(httpRequestMiddleware)

	// Default configuration
	sourceRoot := ""
	if gopath := os.Getenv("GOPATH"); len(gopath) > 0 {
		sourceRoot = filepath.Join(gopath, "src") + "/"
	} else {
		sourceRoot = filepath.Join(runtime.GOROOT(), "src") + "/"
	}
	Config.update(&Configuration{
		APIKey: "",
		Endpoints: Endpoints{
			Notify:   "https://notify.bugsnag.com",
			Sessions: "https://sessions.bugsnag.com",
		},
		Hostname:            "",
		AppType:             "",
		AppVersion:          "",
		AutoCaptureSessions: true,
		ReleaseStage:        "",
		ParamsFilters:       []string{"password", "secret"},
		SourceRoot:          sourceRoot,
		// * for app-engine
		ProjectPackages:     []string{"main*"},
		NotifyReleaseStages: nil,
		Logger:              log.New(os.Stdout, log.Prefix(), log.Flags()),
		PanicHandler:        defaultPanicHandler,
		Transport:           http.DefaultTransport,
	})

	hostname, err := os.Hostname()
	if err == nil {
		Config.Hostname = hostname
	}
}

func startSessionTracking() {
	sessionTrackingConfig.Update(&sessions.SessionTrackingConfiguration{
		APIKey:          Config.APIKey,
		Endpoint:        Config.Endpoints.Sessions,
		Version:         VERSION,
		PublishInterval: DefaultSessionPublishInterval,
		Transport:       Config.Transport,
		ReleaseStage:    Config.ReleaseStage,
		Hostname:        Config.Hostname,
		AppType:         Config.AppType,
		AppVersion:      Config.AppVersion,
		Logger:          Config.Logger,
	})
	if sessionTracker != nil {
		Config.logf(configuredMultipleTimes)
	}
	sessionTracker = sessions.NewSessionTracker(&sessionTrackingConfig)
}
<commit_msg>[fix] Don't make new session tracker if it exists<commit_after>package bugsnag

import (
	"context"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"runtime"
	"sync"
	"time"

	"github.com/bugsnag/bugsnag-go/errors"
	"github.com/bugsnag/bugsnag-go/sessions"

	// Fixes a bug with SHA-384 intermediate certs on some platforms.
	// - https://github.com/bugsnag/bugsnag-go/issues/9
	_ "crypto/sha512"
)

// The current version of bugsnag-go.
const VERSION = "1.3.1"
const configuredMultipleTimes = "WARNING: Bugsnag was configured twice. It is recommended to only call bugsnag.Configure once to ensure consistent session tracking behavior"

var once sync.Once
var middleware middlewareStack

// The configuration for the default bugsnag notifier.
var Config Configuration
var sessionTrackingConfig sessions.SessionTrackingConfiguration

// DefaultSessionPublishInterval defines how often sessions should be sent to
// Bugsnag.
// Deprecated: Exposed for developer sanity in testing. Modify at own risk.
var DefaultSessionPublishInterval = 60 * time.Second
var defaultNotifier = Notifier{&Config, nil}
var sessionTracker sessions.SessionTracker

// Configure Bugsnag. The only required setting is the APIKey, which can be
// obtained by clicking on "Settings" in your Bugsnag dashboard. This function
// is also responsible for installing the global panic handler, so it should be
// called as early as possible in your initialization process.
func Configure(config Configuration) {
	Config.update(&config)
	once.Do(Config.PanicHandler)
	startSessionTracking()
}

// StartSession creates a clone of the context.Context instance with Bugsnag
// session data attached.
func StartSession(ctx context.Context) context.Context {
	return sessionTracker.StartSession(ctx)
}

// Notify sends an error to Bugsnag along with the current stack trace. The
// rawData is used to send extra information along with the error. For example
// you can pass the current http.Request to Bugsnag to see information about it
// in the dashboard, or set the severity of the notification.
func Notify(err error, rawData ...interface{}) error {
	return defaultNotifier.Notify(errors.New(err, 1), rawData...)
}

// AutoNotify logs a panic on a goroutine and then repanics.
// It should only be used in places that have existing panic handlers further
// up the stack. The rawData is used to send extra information along with any
// panics that are handled this way.
// Usage:
//  go func() {
//		defer bugsnag.AutoNotify()
//      // (possibly crashy code)
//  }()
// See also: bugsnag.Recover()
func AutoNotify(rawData ...interface{}) {
	if err := recover(); err != nil {
		severity := defaultNotifier.getDefaultSeverity(rawData, SeverityError)
		state := HandledState{SeverityReasonHandledPanic, severity, true, ""}
		rawData = append([]interface{}{state}, rawData...)
		defaultNotifier.NotifySync(errors.New(err, 2), true, rawData...)
		panic(err)
	}
}

// Recover logs a panic on a goroutine and then recovers.
// The rawData is used to send extra information along with
// any panics that are handled this way
// Usage: defer bugsnag.Recover()
func Recover(rawData ...interface{}) {
	if err := recover(); err != nil {
		severity := defaultNotifier.getDefaultSeverity(rawData, SeverityWarning)
		state := HandledState{SeverityReasonHandledPanic, severity, false, ""}
		rawData = append([]interface{}{state}, rawData...)
		defaultNotifier.Notify(errors.New(err, 2), rawData...)
	}
}

// OnBeforeNotify adds a callback to be run before a notification is sent to
// Bugsnag.  It can be used to modify the event or its MetaData. Changes made
// to the configuration are local to notifying about this event. To prevent the
// event from being sent to Bugsnag return an error, this error will be
// returned from bugsnag.Notify() and the event will not be sent.
func OnBeforeNotify(callback func(event *Event, config *Configuration) error) {
	middleware.OnBeforeNotify(callback)
}

// Handler creates an http Handler that notifies Bugsnag any panics that
// happen. It then repanics so that the default http Server panic handler can
// handle the panic too. The rawData is used to send extra information along
// with any panics that are handled this way.
func Handler(h http.Handler, rawData ...interface{}) http.Handler {
	notifier := New(rawData...)
	if h == nil {
		h = http.DefaultServeMux
	}

	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		defer notifier.AutoNotify(r)
		h.ServeHTTP(w, r)
	})
}

// HandlerFunc creates an http HandlerFunc that notifies Bugsnag about any
// panics that happen. It then repanics so that the default http Server panic
// handler can handle the panic too. The rawData is used to send extra
// information along with any panics that are handled this way. If you have
// already wrapped your http server using bugsnag.Handler() you don't also need
// to wrap each HandlerFunc.
func HandlerFunc(h http.HandlerFunc, rawData ...interface{}) http.HandlerFunc {
	notifier := New(rawData...)

	return func(w http.ResponseWriter, r *http.Request) {
		defer notifier.AutoNotify(r)
		h(w, r)
	}
}

func init() {
	// Set up builtin middlewarez
	OnBeforeNotify(httpRequestMiddleware)

	// Default configuration
	sourceRoot := ""
	if gopath := os.Getenv("GOPATH"); len(gopath) > 0 {
		sourceRoot = filepath.Join(gopath, "src") + "/"
	} else {
		sourceRoot = filepath.Join(runtime.GOROOT(), "src") + "/"
	}
	Config.update(&Configuration{
		APIKey: "",
		Endpoints: Endpoints{
			Notify:   "https://notify.bugsnag.com",
			Sessions: "https://sessions.bugsnag.com",
		},
		Hostname:            "",
		AppType:             "",
		AppVersion:          "",
		AutoCaptureSessions: true,
		ReleaseStage:        "",
		ParamsFilters:       []string{"password", "secret"},
		SourceRoot:          sourceRoot,
		// * for app-engine
		ProjectPackages:     []string{"main*"},
		NotifyReleaseStages: nil,
		Logger:              log.New(os.Stdout, log.Prefix(), log.Flags()),
		PanicHandler:        defaultPanicHandler,
		Transport:           http.DefaultTransport,
	})

	hostname, err := os.Hostname()
	if err == nil {
		Config.Hostname = hostname
	}
}

func startSessionTracking() {
	sessionTrackingConfig.Update(&sessions.SessionTrackingConfiguration{
		APIKey:          Config.APIKey,
		Endpoint:        Config.Endpoints.Sessions,
		Version:         VERSION,
		PublishInterval: DefaultSessionPublishInterval,
		Transport:       Config.Transport,
		ReleaseStage:    Config.ReleaseStage,
		Hostname:        Config.Hostname,
		AppType:         Config.AppType,
		AppVersion:      Config.AppVersion,
		Logger:          Config.Logger,
	})
	if sessionTracker != nil {
		Config.logf(configuredMultipleTimes)
	} else {
		sessionTracker = sessions.NewSessionTracker(&sessionTrackingConfig)
	}
}
<|endoftext|>


<commit_before>package httpauth

import (
    "bytes"
    "database/sql"
    "fmt"
    _ "github.com/go-sql-driver/mysql"
    "os"
    "testing"
)

var (
    sb         SqlAuthBackend
    driverName = "mysql"
    driverInfo = "travis@tcp(127.0.0.1:3306)/test"
)

func TestSqlInit(t *testing.T) {
    con, err := sql.Open(driverName, driverInfo)
    if err != nil {
        t.Errorf("Couldn't set up test sql database: %v", err)
        fmt.Printf("Couldn't set up test sql database: %v\n", err)
        os.Exit(1)
    }
    err = con.Ping()
    if err != nil {
        t.Errorf("Couldn't ping test sql database: %v", err)
        fmt.Printf("Couldn't ping test sql database: %v\n", err)
        // t.Errorf("Couldn't ping test database: %v\n", err)
        os.Exit(1)
    }
    con.Exec("drop table goauth")
}

func TestNewSqlAuthBackend(t *testing.T) {
    sb = NewSqlAuthBackend(driverName, driverInfo)
    if sb.driverName != driverName {
        t.Fatal("Driver name.")
    }
    if sb.dataSourceName != driverInfo {
        t.Fatal("Driver info not saved.")
    }
}

func TestSaveUser_sql(t *testing.T) {
    user2 := UserData{"username2", "email2", []byte("passwordhash2"), "role2"}
    if err := sb.SaveUser(user2); err != nil {
        t.Fatalf("SaveUser sql error: %v", err)
    }

    user := UserData{"username", "email", []byte("passwordhash"), "role"}
    if err := sb.SaveUser(user); err != nil {
        t.Fatalf("SaveUser sql error: %v", err)
    }
}

func TestNewSqlAuthBackend_existing(t *testing.T) {
    b2 := NewSqlAuthBackend(driverName, driverInfo)

    user, ok := b2.User("username")
    if !ok {
        t.Fatal("Secondary backend failed")
    }
    if user.Username != "username" {
        t.Fatal("Username not correct.")
    }
    if user.Email != "email" {
        t.Fatal("User email not correct.")
    }
    if !bytes.Equal(user.Hash, []byte("passwordhash")) {
        t.Fatal("User password not correct.")
    }
}

func TestUser_existing_sql(t *testing.T) {
    if user, ok := sb.User("username"); ok {
        if user.Username != "username" {
            t.Fatal("Username not correct.")
        }
        if user.Email != "email" {
            t.Fatal("User email not correct.")
        }
        if !bytes.Equal(user.Hash, []byte("passwordhash")) {
            t.Fatal("User password not correct.")
        }
    } else {
        t.Fatal("User not found")
    }
    if user, ok := sb.User("username2"); ok {
        if user.Username != "username2" {
            t.Fatal("Username not correct.")
        }
        if user.Email != "email2" {
            t.Fatal("User email not correct.")
        }
        if !bytes.Equal(user.Hash, []byte("passwordhash2")) {
            t.Fatal("User password not correct.")
        }
    } else {
        t.Fatal("User not found")
    }
}

func TestUser_notexisting_sql(t *testing.T) {
    if _, ok := sb.User("notexist"); ok {
        t.Fatal("Not existing user found.")
    }
}

func TestUsers_sql(t *testing.T) {
    var (
        u1 UserData
        u2 UserData
    )
    users := sb.Users()
    if len(users) != 2 {
        t.Fatal("Wrong amount of users found.")
    }
    if users[0].Username == "username" {
        u1 = users[0]
        u2 = users[1]
    } else if users[1].Username == "username" {
        u1 = users[1]
        u2 = users[0]
    } else {
        t.Fatal("One of the users not found.")
    }

    if u1.Username != "username" {
        t.Fatal("Username not correct.")
    }
    if u1.Email != "email" {
        t.Fatal("User email not correct.")
    }
    if !bytes.Equal(u1.Hash, []byte("passwordhash")) {
        t.Fatal("User password not correct.")
    }
    if u2.Username != "username2" {
        t.Fatal("Username not correct.")
    }
    if u2.Email != "email2" {
        t.Fatal("User email not correct.")
    }
    if !bytes.Equal(u2.Hash, []byte("passwordhash2")) {
        t.Fatal("User password not correct.")
    }
}

func TestUpdateUser_sql(t *testing.T) {
    user2 := UserData{"username", "newemail", []byte("newpassword"), "newrole"}
    if err := sb.SaveUser(user2); err != nil {
        t.Fatalf("SaveUser sql error: %v", err)
    }
    u2, ok := sb.User("username")
    if !ok {
        t.Fatal("Updated user not found")
    }
    if u2.Username != "username" {
        t.Fatal("Username not correct.")
    }
    if u2.Email != "newemail" {
        t.Fatal("User email not correct.")
    }
    if u2.Role != "newrole" {
        t.Fatalf("User role not correct: found %v, expected %v", u2.Role, "newrole");
    }
    if !bytes.Equal(u2.Hash, []byte("newpassword")) {
        t.Fatal("User password not correct.")
    }
}

func TestSqlDeleteUser_sql(t *testing.T) {
    if err := sb.DeleteUser("username"); err != nil {
        t.Fatalf("DeleteUser error: %v", err)
    }
    if err := sb.DeleteUser("username"); err != nil {
        t.Fatalf("DeleteUser error: %v", err)
    }

    if err := sb.DeleteUser("username2"); err != nil {
        t.Fatalf("DeleteUser error: %v", err)
    }
}
<commit_msg>Using proper travis database<commit_after>package httpauth

import (
    "bytes"
    "database/sql"
    "fmt"
    _ "github.com/go-sql-driver/mysql"
    "os"
    "testing"
)

var (
    sb         SqlAuthBackend
    driverName = "mysql"
    driverInfo = "travis@tcp(127.0.0.1:3306)/httpauth_test"
)

func TestSqlInit(t *testing.T) {
    con, err := sql.Open(driverName, driverInfo)
    if err != nil {
        t.Errorf("Couldn't set up test sql database: %v", err)
        fmt.Printf("Couldn't set up test sql database: %v\n", err)
        os.Exit(1)
    }
    err = con.Ping()
    if err != nil {
        t.Errorf("Couldn't ping test sql database: %v", err)
        fmt.Printf("Couldn't ping test sql database: %v\n", err)
        // t.Errorf("Couldn't ping test database: %v\n", err)
        os.Exit(1)
    }
    con.Exec("drop table goauth")
}

func TestNewSqlAuthBackend(t *testing.T) {
    sb = NewSqlAuthBackend(driverName, driverInfo)
    if sb.driverName != driverName {
        t.Fatal("Driver name.")
    }
    if sb.dataSourceName != driverInfo {
        t.Fatal("Driver info not saved.")
    }
}

func TestSaveUser_sql(t *testing.T) {
    user2 := UserData{"username2", "email2", []byte("passwordhash2"), "role2"}
    if err := sb.SaveUser(user2); err != nil {
        t.Fatalf("SaveUser sql error: %v", err)
    }

    user := UserData{"username", "email", []byte("passwordhash"), "role"}
    if err := sb.SaveUser(user); err != nil {
        t.Fatalf("SaveUser sql error: %v", err)
    }
}

func TestNewSqlAuthBackend_existing(t *testing.T) {
    b2 := NewSqlAuthBackend(driverName, driverInfo)

    user, ok := b2.User("username")
    if !ok {
        t.Fatal("Secondary backend failed")
    }
    if user.Username != "username" {
        t.Fatal("Username not correct.")
    }
    if user.Email != "email" {
        t.Fatal("User email not correct.")
    }
    if !bytes.Equal(user.Hash, []byte("passwordhash")) {
        t.Fatal("User password not correct.")
    }
}

func TestUser_existing_sql(t *testing.T) {
    if user, ok := sb.User("username"); ok {
        if user.Username != "username" {
            t.Fatal("Username not correct.")
        }
        if user.Email != "email" {
            t.Fatal("User email not correct.")
        }
        if !bytes.Equal(user.Hash, []byte("passwordhash")) {
            t.Fatal("User password not correct.")
        }
    } else {
        t.Fatal("User not found")
    }
    if user, ok := sb.User("username2"); ok {
        if user.Username != "username2" {
            t.Fatal("Username not correct.")
        }
        if user.Email != "email2" {
            t.Fatal("User email not correct.")
        }
        if !bytes.Equal(user.Hash, []byte("passwordhash2")) {
            t.Fatal("User password not correct.")
        }
    } else {
        t.Fatal("User not found")
    }
}

func TestUser_notexisting_sql(t *testing.T) {
    if _, ok := sb.User("notexist"); ok {
        t.Fatal("Not existing user found.")
    }
}

func TestUsers_sql(t *testing.T) {
    var (
        u1 UserData
        u2 UserData
    )
    users := sb.Users()
    if len(users) != 2 {
        t.Fatal("Wrong amount of users found.")
    }
    if users[0].Username == "username" {
        u1 = users[0]
        u2 = users[1]
    } else if users[1].Username == "username" {
        u1 = users[1]
        u2 = users[0]
    } else {
        t.Fatal("One of the users not found.")
    }

    if u1.Username != "username" {
        t.Fatal("Username not correct.")
    }
    if u1.Email != "email" {
        t.Fatal("User email not correct.")
    }
    if !bytes.Equal(u1.Hash, []byte("passwordhash")) {
        t.Fatal("User password not correct.")
    }
    if u2.Username != "username2" {
        t.Fatal("Username not correct.")
    }
    if u2.Email != "email2" {
        t.Fatal("User email not correct.")
    }
    if !bytes.Equal(u2.Hash, []byte("passwordhash2")) {
        t.Fatal("User password not correct.")
    }
}

func TestUpdateUser_sql(t *testing.T) {
    user2 := UserData{"username", "newemail", []byte("newpassword"), "newrole"}
    if err := sb.SaveUser(user2); err != nil {
        t.Fatalf("SaveUser sql error: %v", err)
    }
    u2, ok := sb.User("username")
    if !ok {
        t.Fatal("Updated user not found")
    }
    if u2.Username != "username" {
        t.Fatal("Username not correct.")
    }
    if u2.Email != "newemail" {
        t.Fatal("User email not correct.")
    }
    if u2.Role != "newrole" {
        t.Fatalf("User role not correct: found %v, expected %v", u2.Role, "newrole");
    }
    if !bytes.Equal(u2.Hash, []byte("newpassword")) {
        t.Fatal("User password not correct.")
    }
}

func TestSqlDeleteUser_sql(t *testing.T) {
    if err := sb.DeleteUser("username"); err != nil {
        t.Fatalf("DeleteUser error: %v", err)
    }
    if err := sb.DeleteUser("username"); err != nil {
        t.Fatalf("DeleteUser error: %v", err)
    }

    if err := sb.DeleteUser("username2"); err != nil {
        t.Fatalf("DeleteUser error: %v", err)
    }
}
<|endoftext|>


<commit_before>// Copyright © 2017 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package networkserver

import (
	"math"
	"sort"

	pb_broker "github.com/TheThingsNetwork/api/broker"
	pb_lorawan "github.com/TheThingsNetwork/api/protocol/lorawan"
	"github.com/TheThingsNetwork/ttn/core/band"
	"github.com/TheThingsNetwork/ttn/core/networkserver/device"
	"github.com/brocaar/lorawan"
)

// DefaultADRMargin is the default SNR margin for ADR
var DefaultADRMargin = 15

func maxSNR(frames []*device.Frame) float32 {
	if len(frames) == 0 {
		return 0
	}
	max := frames[0].SNR
	for _, frame := range frames {
		if frame.SNR > max {
			max = frame.SNR
		}
	}
	return max
}

func lossPercentage(frames []*device.Frame) int {
	if len(frames) == 0 {
		return 0
	}
	sentPackets := frames[0].FCnt - frames[len(frames)-1].FCnt + 1
	loss := sentPackets - uint32(len(frames))
	return int(math.Floor((float64(loss) / float64(sentPackets) * 100) + .5))
}

func (n *networkServer) handleUplinkADR(message *pb_broker.DeduplicatedUplinkMessage, dev *device.Device) error {
	lorawanUplinkMAC := message.GetMessage().GetLoRaWAN().GetMACPayload()
	lorawanDownlinkMAC := message.GetResponseTemplate().GetMessage().GetLoRaWAN().GetMACPayload()

	history, err := n.devices.Frames(dev.AppEUI, dev.DevEUI)
	if err != nil {
		return err
	}

	if lorawanUplinkMAC.ADR {
		if err := history.Push(&device.Frame{
			FCnt:         lorawanUplinkMAC.FCnt,
			SNR:          bestSNR(message.GetGatewayMetadata()),
			GatewayCount: uint32(len(message.GatewayMetadata)),
		}); err != nil {
			n.Ctx.WithError(err).Error("Could not push frame for device")
		}
		if dev.ADR.Band == "" {
			dev.ADR.Band = message.GetProtocolMetadata().GetLoRaWAN().GetFrequencyPlan().String()
		}

		if !dev.ADR.SentInitial {
			dev.ADR.SendReq = true        // schedule a LinkADRReq
			lorawanDownlinkMAC.Ack = true // force a downlink
		}
		dataRate := message.GetProtocolMetadata().GetLoRaWAN().GetDataRate()
		if dev.ADR.DataRate != dataRate {
			dev.ADR.DataRate = dataRate
			dev.ADR.SendReq = true // schedule a LinkADRReq
		}
		if lorawanUplinkMAC.ADRAckReq {
			dev.ADR.SendReq = true        // schedule a LinkADRReq
			lorawanDownlinkMAC.Ack = true // force a downlink
		}

		if dev.ADR.SendReq {
			if fp, err := band.Get(dev.ADR.Band); err == nil {
				if drIdx, err := fp.GetDataRateIndexFor(dataRate); err == nil && drIdx == 0 {
					history, _ := n.devices.Frames(dev.AppEUI, dev.DevEUI)
					frames, _ := history.Get()
					if len(frames) >= device.FramesHistorySize {
						lorawanDownlinkMAC.Ack = true // force a downlink
					}
				}
			}
		}

	} else {
		// Clear history and reset settings
		if err := history.Clear(); err != nil {
			return err
		}
		dev.ADR.SendReq = false
		dev.ADR.DataRate = ""
		dev.ADR.TxPower = 0
		dev.ADR.NbTrans = 0
	}

	return nil
}

func (n *networkServer) handleDownlinkADR(message *pb_broker.DownlinkMessage, dev *device.Device) error {
	if !dev.ADR.SendReq {
		return nil
	}

	if dev.ADR.Failed > 0 {
		return nil
	}

	history, err := n.devices.Frames(dev.AppEUI, dev.DevEUI)
	if err != nil {
		return err
	}

	frames, err := history.Get()
	if err != nil {
		return err
	}

	switch {
	case len(frames) == 0:
		return nil
	case len(frames) >= device.FramesHistorySize:
		frames = frames[:device.FramesHistorySize]
	}

	// Check settings
	if dev.ADR.DataRate == "" {
		return nil
	}
	if dev.ADR.Margin == 0 {
		dev.ADR.Margin = DefaultADRMargin
	}
	if dev.ADR.Band == "" {
		return nil
	}
	fp, err := band.Get(dev.ADR.Band)
	if err != nil {
		return err
	}
	if dev.ADR.TxPower == 0 {
		dev.ADR.TxPower = fp.DefaultTXPower
	}
	if dev.ADR.NbTrans == 0 {
		dev.ADR.NbTrans = 1
	}

	adrMargin := float32(dev.ADR.Margin)
	if !dev.ADR.SentInitial {
		adrMargin += 2.5 // Take an extra margin if we don't have enough data yet
	}

	// Calculate ADR settings
	dataRate, txPower, err := fp.ADRSettings(dev.ADR.DataRate, dev.ADR.TxPower, maxSNR(frames), adrMargin)
	if err == band.ErrADRUnavailable {
		return nil
	}
	if err != nil {
		return err
	}
	drIdx, err := fp.GetDataRateIndexFor(dataRate)
	if err != nil {
		return err
	}
	powerIdx, err := fp.GetTxPowerIndexFor(txPower)
	if err != nil {
		powerIdx, _ = fp.GetTxPowerIndexFor(fp.DefaultTXPower)
	}

	var nbTrans = dev.ADR.NbTrans
	if dev.ADR.DataRate == dataRate && dev.ADR.TxPower == txPower && !dev.Options.DisableFCntCheck {
		lossPercentage := lossPercentage(frames)
		switch {
		case lossPercentage <= 5:
			nbTrans--
		case lossPercentage <= 10:
			// don't change
		case lossPercentage <= 30:
			nbTrans++
		default:
			nbTrans += 2
		}
		if nbTrans < 1 {
			nbTrans = 1
		}
		if nbTrans > 3 {
			nbTrans = 3
		}
	}

	if dev.ADR.DataRate == dataRate && dev.ADR.TxPower == txPower && dev.ADR.NbTrans == nbTrans {
		return nil
	}
	dev.ADR.DataRate, dev.ADR.TxPower, dev.ADR.NbTrans = dataRate, txPower, nbTrans

	// Set MAC command
	lorawanDownlinkMAC := message.GetMessage().GetLoRaWAN().GetMACPayload()

	payloads := getAdrReqPayloads(dev, &fp, drIdx, powerIdx)

	if len(payloads) == 0 {
		return nil
	}

	if !dev.ADR.SentInitial {
		dev.ADR.SentInitial = true
	}

	// Remove LinkADRReq if already added
	fOpts := make([]pb_lorawan.MACCommand, 0, len(lorawanDownlinkMAC.FOpts)+len(payloads))
	for _, existing := range lorawanDownlinkMAC.FOpts {
		if existing.CID != uint32(lorawan.LinkADRReq) {
			fOpts = append(fOpts, existing)
		}
	}
	for _, payload := range payloads {
		responsePayload, _ := payload.MarshalBinary()
		fOpts = append(fOpts, pb_lorawan.MACCommand{
			CID:     uint32(lorawan.LinkADRReq),
			Payload: responsePayload,
		})
	}

	lorawanDownlinkMAC.FOpts = fOpts

	return nil
}

func getAdrReqPayloads(dev *device.Device, frequencyPlan *band.FrequencyPlan, drIdx int, powerIdx int) []lorawan.LinkADRReqPayload {
	payloads := []lorawan.LinkADRReqPayload{}
	switch dev.ADR.Band {
	case pb_lorawan.FrequencyPlan_EU_863_870.String():
		payloads = []lorawan.LinkADRReqPayload{
			{
				DataRate: uint8(drIdx),
				TXPower:  uint8(powerIdx),
				Redundancy: lorawan.Redundancy{
					ChMaskCntl: 0,
					NbRep:      uint8(dev.ADR.NbTrans),
				},
			},
		}
		for i, ch := range frequencyPlan.UplinkChannels {
			for _, dr := range ch.DataRates {
				if dr == drIdx {
					payloads[0].ChMask[i] = true
				}
			}
		}
	case pb_lorawan.FrequencyPlan_US_902_928.String(), pb_lorawan.FrequencyPlan_AU_915_928.String():
		// Adapted from https://github.com/brocaar/lorawan/blob/master/band/band_us902_928.go
		payloads = []lorawan.LinkADRReqPayload{
			{
				DataRate: uint8(drIdx),
				TXPower:  uint8(powerIdx),
				Redundancy: lorawan.Redundancy{
					ChMaskCntl: 7,
					NbRep:      uint8(dev.ADR.NbTrans),
				},
			}, // All 125 kHz OFF ChMask applies to channels 64 to 71
		}
		channels := frequencyPlan.GetEnabledUplinkChannels()
		sort.Ints(channels)

		chMaskCntl := -1
		for _, c := range channels {
			// use the ChMask of the first LinkADRReqPayload, besides
			// turning off all 125 kHz this payload contains the ChMask
			// for the last block of channels.
			if c >= 64 {
				payloads[0].ChMask[c%16] = true
				continue
			}

			if c/16 != chMaskCntl {
				chMaskCntl = c / 16
				pl := lorawan.LinkADRReqPayload{
					DataRate: uint8(drIdx),
					TXPower:  uint8(powerIdx),
					Redundancy: lorawan.Redundancy{
						ChMaskCntl: uint8(chMaskCntl),
						NbRep:      uint8(dev.ADR.NbTrans),
					},
				}

				// set the channel mask for this block
				for _, ec := range channels {
					if ec >= chMaskCntl*16 && ec < (chMaskCntl+1)*16 {
						pl.ChMask[ec%16] = true
					}
				}
				payloads = append(payloads, pl)
			}
		}
	}
	return payloads
}
<commit_msg>Trace ADR events<commit_after>// Copyright © 2017 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package networkserver

import (
	"math"
	"sort"

	pb_broker "github.com/TheThingsNetwork/api/broker"
	pb_lorawan "github.com/TheThingsNetwork/api/protocol/lorawan"
	"github.com/TheThingsNetwork/ttn/core/band"
	"github.com/TheThingsNetwork/ttn/core/networkserver/device"
	"github.com/brocaar/lorawan"
)

// DefaultADRMargin is the default SNR margin for ADR
var DefaultADRMargin = 15

func maxSNR(frames []*device.Frame) float32 {
	if len(frames) == 0 {
		return 0
	}
	max := frames[0].SNR
	for _, frame := range frames {
		if frame.SNR > max {
			max = frame.SNR
		}
	}
	return max
}

func lossPercentage(frames []*device.Frame) int {
	if len(frames) == 0 {
		return 0
	}
	sentPackets := frames[0].FCnt - frames[len(frames)-1].FCnt + 1
	loss := sentPackets - uint32(len(frames))
	return int(math.Floor((float64(loss) / float64(sentPackets) * 100) + .5))
}

const ScheduleMACEvent = "schedule mac command"

func (n *networkServer) handleUplinkADR(message *pb_broker.DeduplicatedUplinkMessage, dev *device.Device) error {
	lorawanUplinkMAC := message.GetMessage().GetLoRaWAN().GetMACPayload()
	lorawanDownlinkMAC := message.GetResponseTemplate().GetMessage().GetLoRaWAN().GetMACPayload()

	history, err := n.devices.Frames(dev.AppEUI, dev.DevEUI)
	if err != nil {
		return err
	}

	if lorawanUplinkMAC.ADR {
		if err := history.Push(&device.Frame{
			FCnt:         lorawanUplinkMAC.FCnt,
			SNR:          bestSNR(message.GetGatewayMetadata()),
			GatewayCount: uint32(len(message.GatewayMetadata)),
		}); err != nil {
			n.Ctx.WithError(err).Error("Could not push frame for device")
		}
		if dev.ADR.Band == "" {
			dev.ADR.Band = message.GetProtocolMetadata().GetLoRaWAN().GetFrequencyPlan().String()
		}

		if !dev.ADR.SentInitial {
			dev.ADR.SendReq = true        // schedule a LinkADRReq
			lorawanDownlinkMAC.Ack = true // force a downlink
			message.Trace = message.Trace.WithEvent(ScheduleMACEvent, macCMD, "link-adr", "reason", "initial")
		}
		dataRate := message.GetProtocolMetadata().GetLoRaWAN().GetDataRate()
		if dev.ADR.DataRate != dataRate {
			dev.ADR.DataRate = dataRate
			dev.ADR.SendReq = true // schedule a LinkADRReq
		}
		if lorawanUplinkMAC.ADRAckReq {
			dev.ADR.SendReq = true        // schedule a LinkADRReq
			lorawanDownlinkMAC.Ack = true // force a downlink
			message.Trace = message.Trace.WithEvent(ScheduleMACEvent, macCMD, "link-adr", "reason", "adr-ack-req")
		}

		if dev.ADR.SendReq {
			if fp, err := band.Get(dev.ADR.Band); err == nil {
				if drIdx, err := fp.GetDataRateIndexFor(dataRate); err == nil && drIdx == 0 {
					history, _ := n.devices.Frames(dev.AppEUI, dev.DevEUI)
					frames, _ := history.Get()
					if len(frames) >= device.FramesHistorySize {
						lorawanDownlinkMAC.Ack = true // force a downlink
						message.Trace = message.Trace.WithEvent(ScheduleMACEvent, macCMD, "link-adr", "reason", "avoid high sf")
					}
				}
			}
		}

	} else {
		// Clear history and reset settings
		if err := history.Clear(); err != nil {
			return err
		}
		dev.ADR.SendReq = false
		dev.ADR.DataRate = ""
		dev.ADR.TxPower = 0
		dev.ADR.NbTrans = 0
	}

	return nil
}

func (n *networkServer) handleDownlinkADR(message *pb_broker.DownlinkMessage, dev *device.Device) error {
	if !dev.ADR.SendReq {
		return nil
	}

	if dev.ADR.Failed > 0 {
		return nil
	}

	history, err := n.devices.Frames(dev.AppEUI, dev.DevEUI)
	if err != nil {
		return err
	}

	frames, err := history.Get()
	if err != nil {
		return err
	}

	switch {
	case len(frames) == 0:
		return nil
	case len(frames) >= device.FramesHistorySize:
		frames = frames[:device.FramesHistorySize]
	}

	// Check settings
	if dev.ADR.DataRate == "" {
		return nil
	}
	if dev.ADR.Margin == 0 {
		dev.ADR.Margin = DefaultADRMargin
	}
	if dev.ADR.Band == "" {
		return nil
	}
	fp, err := band.Get(dev.ADR.Band)
	if err != nil {
		return err
	}
	if dev.ADR.TxPower == 0 {
		dev.ADR.TxPower = fp.DefaultTXPower
	}
	if dev.ADR.NbTrans == 0 {
		dev.ADR.NbTrans = 1
	}

	adrMargin := float32(dev.ADR.Margin)
	if !dev.ADR.SentInitial {
		adrMargin += 2.5 // Take an extra margin if we don't have enough data yet
	}

	// Calculate ADR settings
	dataRate, txPower, err := fp.ADRSettings(dev.ADR.DataRate, dev.ADR.TxPower, maxSNR(frames), adrMargin)
	if err == band.ErrADRUnavailable {
		return nil
	}
	if err != nil {
		return err
	}
	drIdx, err := fp.GetDataRateIndexFor(dataRate)
	if err != nil {
		return err
	}
	powerIdx, err := fp.GetTxPowerIndexFor(txPower)
	if err != nil {
		powerIdx, _ = fp.GetTxPowerIndexFor(fp.DefaultTXPower)
	}

	var nbTrans = dev.ADR.NbTrans
	if dev.ADR.DataRate == dataRate && dev.ADR.TxPower == txPower && !dev.Options.DisableFCntCheck {
		lossPercentage := lossPercentage(frames)
		switch {
		case lossPercentage <= 5:
			nbTrans--
		case lossPercentage <= 10:
			// don't change
		case lossPercentage <= 30:
			nbTrans++
		default:
			nbTrans += 2
		}
		if nbTrans < 1 {
			nbTrans = 1
		}
		if nbTrans > 3 {
			nbTrans = 3
		}
	}

	if dev.ADR.DataRate == dataRate && dev.ADR.TxPower == txPower && dev.ADR.NbTrans == nbTrans {
		return nil
	}
	dev.ADR.DataRate, dev.ADR.TxPower, dev.ADR.NbTrans = dataRate, txPower, nbTrans

	// Set MAC command
	lorawanDownlinkMAC := message.GetMessage().GetLoRaWAN().GetMACPayload()

	payloads := getAdrReqPayloads(dev, &fp, drIdx, powerIdx)

	if len(payloads) == 0 {
		return nil
	}

	if !dev.ADR.SentInitial {
		dev.ADR.SentInitial = true
	}

	// Remove LinkADRReq if already added
	fOpts := make([]pb_lorawan.MACCommand, 0, len(lorawanDownlinkMAC.FOpts)+len(payloads))
	for _, existing := range lorawanDownlinkMAC.FOpts {
		if existing.CID != uint32(lorawan.LinkADRReq) {
			fOpts = append(fOpts, existing)
		}
	}
	for _, payload := range payloads {
		responsePayload, _ := payload.MarshalBinary()
		fOpts = append(fOpts, pb_lorawan.MACCommand{
			CID:     uint32(lorawan.LinkADRReq),
			Payload: responsePayload,
		})
	}

	lorawanDownlinkMAC.FOpts = fOpts

	return nil
}

func getAdrReqPayloads(dev *device.Device, frequencyPlan *band.FrequencyPlan, drIdx int, powerIdx int) []lorawan.LinkADRReqPayload {
	payloads := []lorawan.LinkADRReqPayload{}
	switch dev.ADR.Band {
	case pb_lorawan.FrequencyPlan_EU_863_870.String():
		payloads = []lorawan.LinkADRReqPayload{
			{
				DataRate: uint8(drIdx),
				TXPower:  uint8(powerIdx),
				Redundancy: lorawan.Redundancy{
					ChMaskCntl: 0,
					NbRep:      uint8(dev.ADR.NbTrans),
				},
			},
		}
		for i, ch := range frequencyPlan.UplinkChannels {
			for _, dr := range ch.DataRates {
				if dr == drIdx {
					payloads[0].ChMask[i] = true
				}
			}
		}
	case pb_lorawan.FrequencyPlan_US_902_928.String(), pb_lorawan.FrequencyPlan_AU_915_928.String():
		// Adapted from https://github.com/brocaar/lorawan/blob/master/band/band_us902_928.go
		payloads = []lorawan.LinkADRReqPayload{
			{
				DataRate: uint8(drIdx),
				TXPower:  uint8(powerIdx),
				Redundancy: lorawan.Redundancy{
					ChMaskCntl: 7,
					NbRep:      uint8(dev.ADR.NbTrans),
				},
			}, // All 125 kHz OFF ChMask applies to channels 64 to 71
		}
		channels := frequencyPlan.GetEnabledUplinkChannels()
		sort.Ints(channels)

		chMaskCntl := -1
		for _, c := range channels {
			// use the ChMask of the first LinkADRReqPayload, besides
			// turning off all 125 kHz this payload contains the ChMask
			// for the last block of channels.
			if c >= 64 {
				payloads[0].ChMask[c%16] = true
				continue
			}

			if c/16 != chMaskCntl {
				chMaskCntl = c / 16
				pl := lorawan.LinkADRReqPayload{
					DataRate: uint8(drIdx),
					TXPower:  uint8(powerIdx),
					Redundancy: lorawan.Redundancy{
						ChMaskCntl: uint8(chMaskCntl),
						NbRep:      uint8(dev.ADR.NbTrans),
					},
				}

				// set the channel mask for this block
				for _, ec := range channels {
					if ec >= chMaskCntl*16 && ec < (chMaskCntl+1)*16 {
						pl.ChMask[ec%16] = true
					}
				}
				payloads = append(payloads, pl)
			}
		}
	}
	return payloads
}
<|endoftext|>


<commit_before>// Copyright 2015 The Serviced Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build integration

package dfs_test

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os/exec"
	"time"

	. "github.com/control-center/serviced/dfs"
	"github.com/control-center/serviced/domain/host"
	"github.com/control-center/serviced/domain/pool"
	"github.com/control-center/serviced/domain/servicetemplate"
	"github.com/control-center/serviced/volume"
	dockerclient "github.com/fsouza/go-dockerclient"
	"github.com/stretchr/testify/mock"

	. "gopkg.in/check.v1"
)

/*
* Benchmarking backups with some fake data. We'll have Docker and the DFS each
* return a random 100M tar.
 */

const (
	TenantID      = "TENANT"
	SnapshotLabel = "SNAP"
	SnapshotName  = "TENANT_SNAP"
)

var i int

func (s *DFSTestSuite) BenchmarkBackup(c *C) {

	// Create a file to play with
	dir := c.MkDir()
	i += 1
	c.Logf("Creating 100M file of random data: %d", i)
	random := fmt.Sprintf("%s/rand.file", dir)
	tar := fmt.Sprintf("%s/rand.tar", dir)
	exec.Command("/bin/bash", "-c", fmt.Sprintf("cat /dev/urandom | head -c10M > %s", random)).Run()
	exec.Command("tar", "cf", tar, random).Run()

	tardata, _ := ioutil.ReadFile(tar)

	now := time.Now().UTC()

	backupInfo := BackupInfo{
		Templates: []servicetemplate.ServiceTemplate{
			{ID: "test-template-1"},
		},
		BaseImages: []string{"library/repo:tag"},
		Pools: []pool.ResourcePool{
			{ID: "test-pool-1", CreatedAt: time.Now().UTC(), UpdatedAt: time.Now().UTC()},
		},
		Hosts: []host.Host{
			{ID: "test-host-1", CreatedAt: time.Now().UTC(), UpdatedAt: time.Now().UTC()},
		},
		Snapshots: []string{SnapshotName},
		Timestamp: now,
	}
	snapshotInfo := volume.SnapshotInfo{
		Name:     SnapshotName,
		TenantID: TenantID,
		Label:    SnapshotLabel,
		Created:  now,
	}
	allImages := append(backupInfo.BaseImages, "testserver:5000/TENANT/repo:tag")
	imagesbuf := bytes.NewBufferString("")

	c.ResetTimer()

	for i := 0; i < c.N; i++ {
		c.StopTimer()
		imagesbuf.Reset()
		json.NewEncoder(imagesbuf).Encode([]string{"TENANT/repo:tag"})
		vol := s.getVolumeFromSnapshot(SnapshotName, TenantID)
		vol.On("SnapshotInfo", SnapshotName).Return(&snapshotInfo, nil)
		vol.On("ReadMetadata", SnapshotLabel, ImagesMetadataFile).Return(&NopCloser{imagesbuf}, nil)
		c.Logf("ReadMetadata should return %#v", imagesbuf)
		s.registry.On("PullImage", mock.AnythingOfType("<-chan time.Time"), "TENANT/repo:tag").Return(nil)
		s.docker.On("PullImage", "library/repo:tag").Return(nil)
		s.docker.On("FindImage", "library/repo:tag").Return(&dockerclient.Image{}, dockerclient.ErrNoSuchImage)
		s.registry.On("ImagePath", "TENANT/repo:tag").Return("testserver:5000/TENANT/repo:tag", nil)
		vol.On("Export", SnapshotLabel, "", mock.AnythingOfType("*utils.Spool")).Return(nil).Run(func(a mock.Arguments) {
			writer := a.Get(2).(io.Writer)
			_, err := writer.Write(tardata)
			c.Assert(err, IsNil)
		})
		s.docker.On("SaveImages", allImages, mock.AnythingOfType("*utils.Spool")).Return(nil).Run(func(a mock.Arguments) {
			writer := a.Get(1).(io.Writer)
			_, err := writer.Write(tardata)
			c.Assert(err, IsNil)
		})
		buf := bytes.NewBufferString("")
		c.StartTimer()
		err := s.dfs.Backup(backupInfo, buf)
		c.Assert(err, IsNil)
	}
}
<commit_msg>Set to 100M test file<commit_after>// Copyright 2015 The Serviced Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build integration

package dfs_test

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"os/exec"
	"time"

	. "github.com/control-center/serviced/dfs"
	"github.com/control-center/serviced/domain/host"
	"github.com/control-center/serviced/domain/pool"
	"github.com/control-center/serviced/domain/servicetemplate"
	"github.com/control-center/serviced/volume"
	dockerclient "github.com/fsouza/go-dockerclient"
	"github.com/stretchr/testify/mock"

	. "gopkg.in/check.v1"
)

/*
* Benchmarking backups with some fake data. We'll have Docker and the DFS each
* return a random 100M tar.
 */

const (
	TenantID      = "TENANT"
	SnapshotLabel = "SNAP"
	SnapshotName  = "TENANT_SNAP"
)

var i int

func (s *DFSTestSuite) BenchmarkBackup(c *C) {
	//1012318498
	// Create a file to play with
	dir := c.MkDir()
	i += 1
	c.Logf("Creating 100M file of random data: %d", i)
	random := fmt.Sprintf("%s/rand.file", dir)
	tar := fmt.Sprintf("%s/rand.tar", dir)
	exec.Command("/bin/bash", "-c", fmt.Sprintf("cat /dev/urandom | head -c100M > %s", random)).Run()
	exec.Command("tar", "cf", tar, random).Run()

	tardata, _ := ioutil.ReadFile(tar)

	now := time.Now().UTC()

	backupInfo := BackupInfo{
		Templates: []servicetemplate.ServiceTemplate{
			{ID: "test-template-1"},
		},
		BaseImages: []string{"library/repo:tag"},
		Pools: []pool.ResourcePool{
			{ID: "test-pool-1", CreatedAt: time.Now().UTC(), UpdatedAt: time.Now().UTC()},
		},
		Hosts: []host.Host{
			{ID: "test-host-1", CreatedAt: time.Now().UTC(), UpdatedAt: time.Now().UTC()},
		},
		Snapshots: []string{SnapshotName},
		Timestamp: now,
	}
	snapshotInfo := volume.SnapshotInfo{
		Name:     SnapshotName,
		TenantID: TenantID,
		Label:    SnapshotLabel,
		Created:  now,
	}
	allImages := append(backupInfo.BaseImages, "testserver:5000/TENANT/repo:tag")
	imagesbuf := bytes.NewBufferString("")

	c.ResetTimer()

	for i := 0; i < c.N; i++ {
		c.StopTimer()
		imagesbuf.Reset()
		json.NewEncoder(imagesbuf).Encode([]string{"TENANT/repo:tag"})
		vol := s.getVolumeFromSnapshot(SnapshotName, TenantID)
		vol.On("SnapshotInfo", SnapshotName).Return(&snapshotInfo, nil)
		vol.On("ReadMetadata", SnapshotLabel, ImagesMetadataFile).Return(&NopCloser{imagesbuf}, nil)
		c.Logf("ReadMetadata should return %#v", imagesbuf)
		s.registry.On("PullImage", mock.AnythingOfType("<-chan time.Time"), "TENANT/repo:tag").Return(nil)
		s.docker.On("PullImage", "library/repo:tag").Return(nil)
		s.docker.On("FindImage", "library/repo:tag").Return(&dockerclient.Image{}, dockerclient.ErrNoSuchImage)
		s.registry.On("ImagePath", "TENANT/repo:tag").Return("testserver:5000/TENANT/repo:tag", nil)
		vol.On("Export", SnapshotLabel, "", mock.AnythingOfType("*utils.Spool")).Return(nil).Run(func(a mock.Arguments) {
			writer := a.Get(2).(io.Writer)
			_, err := writer.Write(tardata)
			c.Assert(err, IsNil)
		})
		s.docker.On("SaveImages", allImages, mock.AnythingOfType("*utils.Spool")).Return(nil).Run(func(a mock.Arguments) {
			writer := a.Get(1).(io.Writer)
			_, err := writer.Write(tardata)
			c.Assert(err, IsNil)
		})
		buf := bytes.NewBufferString("")
		c.StartTimer()
		err := s.dfs.Backup(backupInfo, buf)
		c.Assert(err, IsNil)
	}
}
<|endoftext|>


<commit_before>package docker

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"path"
	"strings"
	"time"
)

type Builder struct {
	runtime      *Runtime
	repositories *TagStore
	graph        *Graph
}

func NewBuilder(runtime *Runtime) *Builder {
	return &Builder{
		runtime:      runtime,
		graph:        runtime.graph,
		repositories: runtime.repositories,
	}
}

func (builder *Builder) mergeConfig(userConf, imageConf *Config) {
	if userConf.Hostname != "" {
		userConf.Hostname = imageConf.Hostname
	}
	if userConf.User != "" {
		userConf.User = imageConf.User
	}
	if userConf.Memory == 0 {
		userConf.Memory = imageConf.Memory
	}
	if userConf.MemorySwap == 0 {
		userConf.MemorySwap = imageConf.MemorySwap
	}
	if userConf.PortSpecs == nil || len(userConf.PortSpecs) == 0 {
		userConf.PortSpecs = imageConf.PortSpecs
	}
	if !userConf.Tty {
		userConf.Tty = userConf.Tty
	}
	if !userConf.OpenStdin {
		userConf.OpenStdin = imageConf.OpenStdin
	}
	if !userConf.StdinOnce {
		userConf.StdinOnce = imageConf.StdinOnce
	}
	if userConf.Env == nil || len(userConf.Env) == 0 {
		userConf.Env = imageConf.Env
	}
	if userConf.Cmd == nil || len(userConf.Cmd) == 0 {
		userConf.Cmd = imageConf.Cmd
	}
	if userConf.Dns == nil || len(userConf.Dns) == 0 {
		userConf.Dns = imageConf.Dns
	}
}

func (builder *Builder) Create(config *Config) (*Container, error) {
	// Lookup image
	img, err := builder.repositories.LookupImage(config.Image)
	if err != nil {
		return nil, err
	}

	if img.Config != nil {
		builder.mergeConfig(config, img.Config)
	}

	if config.Cmd == nil {
		return nil, fmt.Errorf("No command specified")
	}

	// Generate id
	id := GenerateId()
	// Generate default hostname
	// FIXME: the lxc template no longer needs to set a default hostname
	if config.Hostname == "" {
		config.Hostname = id[:12]
	}

	container := &Container{
		// FIXME: we should generate the ID here instead of receiving it as an argument
		Id:              id,
		Created:         time.Now(),
		Path:            config.Cmd[0],
		Args:            config.Cmd[1:], //FIXME: de-duplicate from config
		Config:          config,
		Image:           img.Id, // Always use the resolved image id
		NetworkSettings: &NetworkSettings{},
		// FIXME: do we need to store this in the container?
		SysInitPath: sysInitPath,
	}
	container.root = builder.runtime.containerRoot(container.Id)
	// Step 1: create the container directory.
	// This doubles as a barrier to avoid race conditions.
	if err := os.Mkdir(container.root, 0700); err != nil {
		return nil, err
	}

	// If custom dns exists, then create a resolv.conf for the container
	if len(config.Dns) > 0 {
		container.ResolvConfPath = path.Join(container.root, "resolv.conf")
		f, err := os.Create(container.ResolvConfPath)
		if err != nil {
			return nil, err
		}
		defer f.Close()
		for _, dns := range config.Dns {
			if _, err := f.Write([]byte("nameserver " + dns + "\n")); err != nil {
				return nil, err
			}
		}
	} else {
		container.ResolvConfPath = "/etc/resolv.conf"
	}

	// Step 2: save the container json
	if err := container.ToDisk(); err != nil {
		return nil, err
	}
	// Step 3: register the container
	if err := builder.runtime.Register(container); err != nil {
		return nil, err
	}
	return container, nil
}

// Commit creates a new filesystem image from the current state of a container.
// The image can optionally be tagged into a repository
func (builder *Builder) Commit(container *Container, repository, tag, comment, author string, config *Config) (*Image, error) {
	// FIXME: freeze the container before copying it to avoid data corruption?
	// FIXME: this shouldn't be in commands.
	rwTar, err := container.ExportRw()
	if err != nil {
		return nil, err
	}
	// Create a new image from the container's base layers + a new layer from container changes
	img, err := builder.graph.Create(rwTar, container, comment, author, config)
	if err != nil {
		return nil, err
	}
	// Register the image if needed
	if repository != "" {
		if err := builder.repositories.Set(repository, tag, img.Id, true); err != nil {
			return img, err
		}
	}
	return img, nil
}

func (builder *Builder) clearTmp(containers, images map[string]struct{}) {
	for c := range containers {
		tmp := builder.runtime.Get(c)
		builder.runtime.Destroy(tmp)
		Debugf("Removing container %s", c)
	}
	for i := range images {
		builder.runtime.graph.Delete(i)
		Debugf("Removing image %s", i)
	}
}

func (builder *Builder) getCachedImage(image *Image, config *Config) (*Image, error) {
	// Retrieve all images
	images, err := builder.graph.All()
	if err != nil {
		return nil, err
	}

	// Store the tree in a map of map (map[parentId][childId])
	imageMap := make(map[string]map[string]struct{})
	for _, img := range images {
		if _, exists := imageMap[img.Parent]; !exists {
			imageMap[img.Parent] = make(map[string]struct{})
		}
		imageMap[img.Parent][img.Id] = struct{}{}
	}

	// Loop on the children of the given image and check the config
	for elem := range imageMap[image.Id] {
		img, err := builder.graph.Get(elem)
		if err != nil {
			return nil, err
		}
		if CompareConfig(&img.ContainerConfig, config) {
			return img, nil
		}
	}
	return nil, nil
}

func (builder *Builder) Build(dockerfile io.Reader, stdout io.Writer) (*Image, error) {
	var (
		image, base   *Image
		config        *Config
		maintainer    string
		tmpContainers map[string]struct{} = make(map[string]struct{})
		tmpImages     map[string]struct{} = make(map[string]struct{})
	)
	defer builder.clearTmp(tmpContainers, tmpImages)

	file := bufio.NewReader(dockerfile)
	for {
		line, err := file.ReadString('\n')
		if err != nil {
			if err == io.EOF {
				break
			}
			return nil, err
		}
		line = strings.Replace(strings.TrimSpace(line), "	", " ", 1)
		// Skip comments and empty line
		if len(line) == 0 || line[0] == '#' {
			continue
		}
		tmp := strings.SplitN(line, " ", 2)
		if len(tmp) != 2 {
			return nil, fmt.Errorf("Invalid Dockerfile format")
		}
		instruction := strings.Trim(tmp[0], " ")
		arguments := strings.Trim(tmp[1], " ")
		switch strings.ToLower(instruction) {
		case "from":
			fmt.Fprintf(stdout, "FROM %s\n", arguments)
			image, err = builder.runtime.repositories.LookupImage(arguments)
			if err != nil {
				if builder.runtime.graph.IsNotExist(err) {

					var tag, remote string
					if strings.Contains(arguments, ":") {
						remoteParts := strings.Split(arguments, ":")
						tag = remoteParts[1]
						remote = remoteParts[0]
					} else {
						remote = arguments
					}

					if err := builder.runtime.graph.PullRepository(stdout, remote, tag, builder.runtime.repositories, builder.runtime.authConfig); err != nil {
						return nil, err
					}

					image, err = builder.runtime.repositories.LookupImage(arguments)
					if err != nil {
						return nil, err
					}
				} else {
					return nil, err
				}
			}
			config = &Config{}

			break
		case "maintainer":
			fmt.Fprintf(stdout, "MAINTAINER %s\n", arguments)
			maintainer = arguments
			break
		case "run":
			fmt.Fprintf(stdout, "RUN %s\n", arguments)
			if image == nil {
				return nil, fmt.Errorf("Please provide a source image with `from` prior to run")
			}
			config, err := ParseRun([]string{image.Id, "/bin/sh", "-c", arguments}, nil, builder.runtime.capabilities)
			if err != nil {
				return nil, err
			}

			if cache, err := builder.getCachedImage(image, config); err != nil {
				return nil, err
			} else if cache != nil {
				image = cache
				fmt.Fprintf(stdout, "===> %s\n", image.ShortId())
				break
			}

			// Create the container and start it
			c, err := builder.Create(config)
			if err != nil {
				return nil, err
			}
			if err := c.Start(); err != nil {
				return nil, err
			}
			tmpContainers[c.Id] = struct{}{}

			// Wait for it to finish
			if result := c.Wait(); result != 0 {
				return nil, fmt.Errorf("!!! '%s' return non-zero exit code '%d'. Aborting.", arguments, result)
			}

			// Commit the container
			base, err = builder.Commit(c, "", "", "", maintainer, nil)
			if err != nil {
				return nil, err
			}
			tmpImages[base.Id] = struct{}{}

			fmt.Fprintf(stdout, "===> %s\n", base.ShortId())

			// use the base as the new image
			image = base

			break
		case "cmd":
			fmt.Fprintf(stdout, "CMD %s\n", arguments)

			// Create the container and start it
			c, err := builder.Create(&Config{Image: image.Id, Cmd: []string{"", ""}})
			if err != nil {
				return nil, err
			}
			if err := c.Start(); err != nil {
				return nil, err
			}
			tmpContainers[c.Id] = struct{}{}

			cmd := []string{}
			if err := json.Unmarshal([]byte(arguments), &cmd); err != nil {
				return nil, err
			}
			config.Cmd = cmd

			// Commit the container
			base, err = builder.Commit(c, "", "", "", maintainer, config)
			if err != nil {
				return nil, err
			}
			tmpImages[base.Id] = struct{}{}

			fmt.Fprintf(stdout, "===> %s\n", base.ShortId())
			image = base
			break
		case "expose":
			ports := strings.Split(arguments, " ")

			fmt.Fprintf(stdout, "EXPOSE %v\n", ports)
			if image == nil {
				return nil, fmt.Errorf("Please provide a source image with `from` prior to copy")
			}

			// Create the container and start it
			c, err := builder.Create(&Config{Image: image.Id, Cmd: []string{"", ""}})
			if err != nil {
				return nil, err
			}
			if err := c.Start(); err != nil {
				return nil, err
			}
			tmpContainers[c.Id] = struct{}{}

			config.PortSpecs = append(ports, config.PortSpecs...)

			// Commit the container
			base, err = builder.Commit(c, "", "", "", maintainer, config)
			if err != nil {
				return nil, err
			}
			tmpImages[base.Id] = struct{}{}

			fmt.Fprintf(stdout, "===> %s\n", base.ShortId())
			image = base
			break
		case "insert":
			if image == nil {
				return nil, fmt.Errorf("Please provide a source image with `from` prior to copy")
			}
			tmp = strings.SplitN(arguments, " ", 2)
			if len(tmp) != 2 {
				return nil, fmt.Errorf("Invalid INSERT format")
			}
			sourceUrl := strings.Trim(tmp[0], " ")
			destPath := strings.Trim(tmp[1], " ")
			fmt.Fprintf(stdout, "COPY %s to %s in %s\n", sourceUrl, destPath, base.ShortId())

			file, err := Download(sourceUrl, stdout)
			if err != nil {
				return nil, err
			}
			defer file.Body.Close()

			config, err := ParseRun([]string{base.Id, "echo", "insert", sourceUrl, destPath}, nil, builder.runtime.capabilities)
			if err != nil {
				return nil, err
			}
			c, err := builder.Create(config)
			if err != nil {
				return nil, err
			}

			if err := c.Start(); err != nil {
				return nil, err
			}

			// Wait for echo to finish
			if result := c.Wait(); result != 0 {
				return nil, fmt.Errorf("!!! '%s' return non-zero exit code '%d'. Aborting.", arguments, result)
			}

			if err := c.Inject(file.Body, destPath); err != nil {
				return nil, err
			}

			base, err = builder.Commit(c, "", "", "", maintainer, nil)
			if err != nil {
				return nil, err
			}
			fmt.Fprintf(stdout, "===> %s\n", base.ShortId())

			image = base

			break
		default:
			fmt.Fprintf(stdout, "Skipping unknown instruction %s\n", strings.ToUpper(instruction))
		}
	}
	if image != nil {
		// The build is successful, keep the temporary containers and images
		for i := range tmpImages {
			delete(tmpImages, i)
		}
		for i := range tmpContainers {
			delete(tmpContainers, i)
		}
		fmt.Fprintf(stdout, "Build finished. image id: %s\n", image.ShortId())
		return image, nil
	}
	return nil, fmt.Errorf("An error occured during the build\n")
}
<commit_msg>Moving runtime.Create to builder.Create<commit_after>package docker

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"path"
	"strings"
	"time"
)

type Builder struct {
	runtime      *Runtime
	repositories *TagStore
	graph        *Graph
}

func NewBuilder(runtime *Runtime) *Builder {
	return &Builder{
		runtime:      runtime,
		graph:        runtime.graph,
		repositories: runtime.repositories,
	}
}

func (builder *Builder) mergeConfig(userConf, imageConf *Config) {
	if userConf.Hostname != "" {
		userConf.Hostname = imageConf.Hostname
	}
	if userConf.User != "" {
		userConf.User = imageConf.User
	}
	if userConf.Memory == 0 {
		userConf.Memory = imageConf.Memory
	}
	if userConf.MemorySwap == 0 {
		userConf.MemorySwap = imageConf.MemorySwap
	}
	if userConf.PortSpecs == nil || len(userConf.PortSpecs) == 0 {
		userConf.PortSpecs = imageConf.PortSpecs
	}
	if !userConf.Tty {
		userConf.Tty = userConf.Tty
	}
	if !userConf.OpenStdin {
		userConf.OpenStdin = imageConf.OpenStdin
	}
	if !userConf.StdinOnce {
		userConf.StdinOnce = imageConf.StdinOnce
	}
	if userConf.Env == nil || len(userConf.Env) == 0 {
		userConf.Env = imageConf.Env
	}
	if userConf.Cmd == nil || len(userConf.Cmd) == 0 {
		userConf.Cmd = imageConf.Cmd
	}
	if userConf.Dns == nil || len(userConf.Dns) == 0 {
		userConf.Dns = imageConf.Dns
	}
}

func (builder *Builder) Create(config *Config) (*Container, error) {
	// Lookup image
	img, err := builder.repositories.LookupImage(config.Image)
	if err != nil {
		return nil, err
	}

	if img.Config != nil {
		builder.mergeConfig(config, img.Config)
	}

	if config.Cmd == nil {
		return nil, fmt.Errorf("No command specified")
	}

	// Generate id
	id := GenerateId()
	// Generate default hostname
	// FIXME: the lxc template no longer needs to set a default hostname
	if config.Hostname == "" {
		config.Hostname = id[:12]
	}

	container := &Container{
		// FIXME: we should generate the ID here instead of receiving it as an argument
		Id:              id,
		Created:         time.Now(),
		Path:            config.Cmd[0],
		Args:            config.Cmd[1:], //FIXME: de-duplicate from config
		Config:          config,
		Image:           img.Id, // Always use the resolved image id
		NetworkSettings: &NetworkSettings{},
		// FIXME: do we need to store this in the container?
		SysInitPath: sysInitPath,
	}
	container.root = builder.runtime.containerRoot(container.Id)
	// Step 1: create the container directory.
	// This doubles as a barrier to avoid race conditions.
	if err := os.Mkdir(container.root, 0700); err != nil {
		return nil, err
	}

	// If custom dns exists, then create a resolv.conf for the container
	if len(config.Dns) > 0 {
		container.ResolvConfPath = path.Join(container.root, "resolv.conf")
		f, err := os.Create(container.ResolvConfPath)
		if err != nil {
			return nil, err
		}
		defer f.Close()
		for _, dns := range config.Dns {
			if _, err := f.Write([]byte("nameserver " + dns + "\n")); err != nil {
				return nil, err
			}
		}
	} else {
		container.ResolvConfPath = "/etc/resolv.conf"
	}

	// Step 2: save the container json
	if err := container.ToDisk(); err != nil {
		return nil, err
	}
	// Step 3: register the container
	if err := builder.runtime.Register(container); err != nil {
		return nil, err
	}
	return container, nil
}

// Commit creates a new filesystem image from the current state of a container.
// The image can optionally be tagged into a repository
func (builder *Builder) Commit(container *Container, repository, tag, comment, author string, config *Config) (*Image, error) {
	// FIXME: freeze the container before copying it to avoid data corruption?
	// FIXME: this shouldn't be in commands.
	rwTar, err := container.ExportRw()
	if err != nil {
		return nil, err
	}
	// Create a new image from the container's base layers + a new layer from container changes
	img, err := builder.graph.Create(rwTar, container, comment, author, config)
	if err != nil {
		return nil, err
	}
	// Register the image if needed
	if repository != "" {
		if err := builder.repositories.Set(repository, tag, img.Id, true); err != nil {
			return img, err
		}
	}
	return img, nil
}

func (builder *Builder) clearTmp(containers, images map[string]struct{}) {
	for c := range containers {
		tmp := builder.runtime.Get(c)
		builder.runtime.Destroy(tmp)
		Debugf("Removing container %s", c)
	}
	for i := range images {
		builder.runtime.graph.Delete(i)
		Debugf("Removing image %s", i)
	}
}

func (builder *Builder) getCachedImage(image *Image, config *Config) (*Image, error) {
	// Retrieve all images
	images, err := builder.graph.All()
	if err != nil {
		return nil, err
	}

	// Store the tree in a map of map (map[parentId][childId])
	imageMap := make(map[string]map[string]struct{})
	for _, img := range images {
		if _, exists := imageMap[img.Parent]; !exists {
			imageMap[img.Parent] = make(map[string]struct{})
		}
		imageMap[img.Parent][img.Id] = struct{}{}
	}

	// Loop on the children of the given image and check the config
	for elem := range imageMap[image.Id] {
		img, err := builder.graph.Get(elem)
		if err != nil {
			return nil, err
		}
		if CompareConfig(&img.ContainerConfig, config) {
			return img, nil
		}
	}
	return nil, nil
}

func (builder *Builder) Build(dockerfile io.Reader, stdout io.Writer) (*Image, error) {
	var (
		image, base   *Image
		config        *Config
		maintainer    string
		tmpContainers map[string]struct{} = make(map[string]struct{})
		tmpImages     map[string]struct{} = make(map[string]struct{})
	)
	defer builder.clearTmp(tmpContainers, tmpImages)

	file := bufio.NewReader(dockerfile)
	for {
		line, err := file.ReadString('\n')
		if err != nil {
			if err == io.EOF {
				break
			}
			return nil, err
		}
		line = strings.Replace(strings.TrimSpace(line), "	", " ", 1)
		// Skip comments and empty line
		if len(line) == 0 || line[0] == '#' {
			continue
		}
		tmp := strings.SplitN(line, " ", 2)
		if len(tmp) != 2 {
			return nil, fmt.Errorf("Invalid Dockerfile format")
		}
		instruction := strings.Trim(tmp[0], " ")
		arguments := strings.Trim(tmp[1], " ")
		switch strings.ToLower(instruction) {
		case "from":
			fmt.Fprintf(stdout, "FROM %s\n", arguments)
			image, err = builder.runtime.repositories.LookupImage(arguments)
			if err != nil {
				if builder.runtime.graph.IsNotExist(err) {

					var tag, remote string
					if strings.Contains(arguments, ":") {
						remoteParts := strings.Split(arguments, ":")
						tag = remoteParts[1]
						remote = remoteParts[0]
					} else {
						remote = arguments
					}

					if err := builder.runtime.graph.PullRepository(stdout, remote, tag, builder.runtime.repositories, builder.runtime.authConfig); err != nil {
						return nil, err
					}

					image, err = builder.runtime.repositories.LookupImage(arguments)
					if err != nil {
						return nil, err
					}
				} else {
					return nil, err
				}
			}
			config = &Config{}

			break
		case "maintainer":
			fmt.Fprintf(stdout, "MAINTAINER %s\n", arguments)
			maintainer = arguments
			break
		case "run":
			fmt.Fprintf(stdout, "RUN %s\n", arguments)
			if image == nil {
				return nil, fmt.Errorf("Please provide a source image with `from` prior to run")
			}
			config, err := ParseRun([]string{image.Id, "/bin/sh", "-c", arguments}, nil, builder.runtime.capabilities)
			if err != nil {
				return nil, err
			}

			if cache, err := builder.getCachedImage(image, config); err != nil {
				return nil, err
			} else if cache != nil {
				image = cache
				fmt.Fprintf(stdout, "===> %s\n", image.ShortId())
				break
			}
			config, err := ParseRun([]string{image.Id, "/bin/sh", "-c", tmp[1]}, nil, builder.runtime.capabilities)
			if err != nil {
				return err
			}

			// Create the container and start it
			c, err := builder.Create(config)
			if err != nil {
				return nil, err
			}
			if err := c.Start(); err != nil {
				return nil, err
			}
			if err := c.Start(); err != nil {
				return err
			}
			tmpContainers[c.Id] = struct{}{}

			// Wait for it to finish
			if result := c.Wait(); result != 0 {
				return nil, fmt.Errorf("!!! '%s' return non-zero exit code '%d'. Aborting.", arguments, result)
			}

			// Commit the container
			base, err = builder.Commit(c, "", "", "", maintainer, nil)
			if err != nil {
				return nil, err
			}
			tmpImages[base.Id] = struct{}{}

			fmt.Fprintf(stdout, "===> %s\n", base.ShortId())

			// use the base as the new image
			image = base

			break
		case "cmd":
			fmt.Fprintf(stdout, "CMD %s\n", arguments)

			// Create the container and start it
			c, err := builder.Create(&Config{Image: image.Id, Cmd: []string{"", ""}})
			if err != nil {
				return nil, err
			}
			if err := c.Start(); err != nil {
				return nil, err
			}
			tmpContainers[c.Id] = struct{}{}

			cmd := []string{}
			if err := json.Unmarshal([]byte(arguments), &cmd); err != nil {
				return nil, err
			}
			config.Cmd = cmd

			// Commit the container
			base, err = builder.Commit(c, "", "", "", maintainer, config)
			if err != nil {
				return nil, err
			}
			tmpImages[base.Id] = struct{}{}

			fmt.Fprintf(stdout, "===> %s\n", base.ShortId())
			image = base
			break
		case "expose":
			ports := strings.Split(arguments, " ")

			fmt.Fprintf(stdout, "EXPOSE %v\n", ports)
			if image == nil {
				return nil, fmt.Errorf("Please provide a source image with `from` prior to copy")
			}

			// Create the container and start it
			c, err := builder.Create(&Config{Image: image.Id, Cmd: []string{"", ""}})
			if err != nil {
				return nil, err
			}
			if err := c.Start(); err != nil {
				return nil, err
			}
			tmpContainers[c.Id] = struct{}{}

			config.PortSpecs = append(ports, config.PortSpecs...)

			// Commit the container
			base, err = builder.Commit(c, "", "", "", maintainer, config)
			if err != nil {
				return nil, err
			}
			tmpImages[base.Id] = struct{}{}

			fmt.Fprintf(stdout, "===> %s\n", base.ShortId())
			image = base
			break
		case "insert":
			if image == nil {
				return nil, fmt.Errorf("Please provide a source image with `from` prior to copy")
			}
			tmp = strings.SplitN(arguments, " ", 2)
			if len(tmp) != 2 {
				return nil, fmt.Errorf("Invalid INSERT format")
			}
			sourceUrl := strings.Trim(tmp[0], " ")
			destPath := strings.Trim(tmp[1], " ")
			fmt.Fprintf(stdout, "COPY %s to %s in %s\n", sourceUrl, destPath, base.ShortId())

			file, err := Download(sourceUrl, stdout)
			if err != nil {
				return nil, err
			}
			defer file.Body.Close()

			config, err := ParseRun([]string{base.Id, "echo", "insert", sourceUrl, destPath}, nil, builder.runtime.capabilities)
			if err != nil {
				return nil, err
			}
			c, err := builder.Create(config)
			if err != nil {
				return nil, err
			}

			if err := c.Start(); err != nil {
				return nil, err
			}

			// Wait for echo to finish
			if result := c.Wait(); result != 0 {
				return nil, fmt.Errorf("!!! '%s' return non-zero exit code '%d'. Aborting.", arguments, result)
			}

			if err := c.Inject(file.Body, destPath); err != nil {
				return nil, err
			}

			base, err = builder.Commit(c, "", "", "", maintainer, nil)
			if err != nil {
				return nil, err
			}
			fmt.Fprintf(stdout, "===> %s\n", base.ShortId())

			image = base

			break
		default:
			fmt.Fprintf(stdout, "Skipping unknown instruction %s\n", strings.ToUpper(instruction))
		}
	}
	if image != nil {
		// The build is successful, keep the temporary containers and images
		for i := range tmpImages {
			delete(tmpImages, i)
		}
		for i := range tmpContainers {
			delete(tmpContainers, i)
		}
		fmt.Fprintf(stdout, "Build finished. image id: %s\n", image.ShortId())
		return image, nil
	}
	return nil, fmt.Errorf("An error occured during the build\n")
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"log"
	"time"
)

type Galera struct {
	Conf   ConfGalera
	Status Status
	lg     log.Logger
}

type ConfGalera struct {
	Enabled     bool
	Interval    int
	User        string
	Pass        string
	Host        string
	Port        int
	Local_state int
	Sst_method  string
}

// You do not want to change these queries, to avoid injections
const wsrep_local_state_query = "show global status where variable_name = ?"
const wsrep_sst_method_query = "show global variables where variable_name = ?"

func (g *Galera) check() {
	for ; ; time.Sleep(time.Duration(g.Conf.Interval) * time.Second) {
		var wsrep_sst_method string
		var wsrep_local_state int
		var varName string

		db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:%d)/?timeout=%ds&readTimeout=%ds", g.Conf.User, g.Conf.Pass, g.Conf.Host, g.Conf.Port, g.Conf.Interval/2+1, g.Conf.Interval/2+1))
		if err != nil {
			g.lg.Println("Timeout while connecting to mysql", err.Error())
			g.Status.PartOfCluster = false
			g.Status.Timestamp = time.Now()
			db.Close()
			continue
		}

		err = db.QueryRow(wsrep_local_state_query, "wsrep_local_state").Scan(&varName, &wsrep_local_state)
		if err != nil {
			g.lg.Println("Error querying "+wsrep_local_state_query+": ", err.Error())
			g.Status.PartOfCluster = false

		} else {
			err = db.QueryRow(wsrep_sst_method_query, "wsrep_sst_method").Scan(&varName, &wsrep_sst_method)
			if err != nil {
				g.lg.Println("Error querying "+wsrep_sst_method_query+": ", err.Error())
				g.Status.PartOfCluster = false
			} else {
				if wsrep_local_state == g.Conf.Local_state && wsrep_sst_method == g.Conf.Sst_method {
					g.Status.PartOfCluster = true
				} else {
					g.lg.Printf("wsrep_local_state is %s, but should be %d", wsrep_local_state, g.Conf.Local_state)
					g.lg.Printf("wsrep_sst_method is %s, but should be %d", wsrep_sst_method, g.Conf.Sst_method)
					g.Status.PartOfCluster = false
				}
			}
		}
		g.Status.Timestamp = time.Now()
		db.Close()
	}
}
<commit_msg>Fix of galera's error message<commit_after>package main

import (
	"database/sql"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"log"
	"time"
)

type Galera struct {
	Conf   ConfGalera
	Status Status
	lg     log.Logger
}

type ConfGalera struct {
	Enabled     bool
	Interval    int
	User        string
	Pass        string
	Host        string
	Port        int
	Local_state int
	Sst_method  string
}

// You do not want to change these queries, to avoid injections
const wsrep_local_state_query = "show global status where variable_name = ?"
const wsrep_sst_method_query = "show global variables where variable_name = ?"

func (g *Galera) check() {
	for ; ; time.Sleep(time.Duration(g.Conf.Interval) * time.Second) {
		var wsrep_sst_method string
		var wsrep_local_state int
		var varName string

		db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:%d)/?timeout=%ds&readTimeout=%ds", g.Conf.User, g.Conf.Pass, g.Conf.Host, g.Conf.Port, g.Conf.Interval/2+1, g.Conf.Interval/2+1))
		if err != nil {
			g.lg.Println("Timeout while connecting to mysql", err.Error())
			g.Status.PartOfCluster = false
			g.Status.Timestamp = time.Now()
			db.Close()
			continue
		}

		err = db.QueryRow(wsrep_local_state_query, "wsrep_local_state").Scan(&varName, &wsrep_local_state)
		if err != nil {
			g.lg.Println("Error querying "+wsrep_local_state_query+": ", err.Error())
			g.Status.PartOfCluster = false

		} else {
			err = db.QueryRow(wsrep_sst_method_query, "wsrep_sst_method").Scan(&varName, &wsrep_sst_method)
			if err != nil {
				g.lg.Println("Error querying "+wsrep_sst_method_query+": ", err.Error())
				g.Status.PartOfCluster = false
			} else {
				if wsrep_local_state == g.Conf.Local_state && wsrep_sst_method == g.Conf.Sst_method {
					g.Status.PartOfCluster = true
				} else {
					g.lg.Printf("wsrep_local_state is %s, but should be %d", wsrep_local_state, g.Conf.Local_state)
					g.lg.Printf("wsrep_sst_method is %s, but should be %s", wsrep_sst_method, g.Conf.Sst_method)
					g.Status.PartOfCluster = false
				}
			}
		}
		g.Status.Timestamp = time.Now()
		db.Close()
	}
}
<|endoftext|>


<commit_before>package localfile

import (
	"github.com/hashicorp/terraform/helper/schema"
	"os"
  "io/ioutil"
  "path/filepath"
  "fmt"
)

func resourceLocalFile() *schema.Resource {
	return &schema.Resource{
		Create: resourceLocalFileCreate,
		Read: resourceLocalFileRead,
		Update: nil,
		Delete: resourceLocalFileDelete,
		Schema: map[string]*schema.Schema{
			"path": &schema.Schema{
				Type: schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"content": &schema.Schema{
				Type: schema.TypeString,
				Required: true,
				ForceNew: true,
			},
		},
		Exists: resourceLocalFileExists,
	}
}

type localfileCfg struct {
	Path string
	Content string
}

func newLocalFileCfg(rd *schema.ResourceData) localfileCfg {
	return localfileCfg{
		Path: rd.Get("path").(string),
		Content: rd.Get("content").(string),
	}
}

func resourceLocalFileCreate(rd *schema.ResourceData, _ interface{}) error {
	config := newLocalFileCfg(rd)
  basedir := filepath.Dir(config.Path)

  fmt.Println(basedir, "foo" )
  err := os.MkdirAll(basedir, 0755)
  if err != nil {
    return err
  }

	f, err := os.Create(config.Path)
	if err != nil {
		return err
	}
	defer f.Close()
	rd.SetId(config.Path)

	_, err = f.WriteString(config.Content)
	f.Sync()
	return nil
}

func resourceLocalFileDelete(rd *schema.ResourceData, _ interface{}) error {
	config := newLocalFileCfg(rd)
	return os.Remove(config.Path)
}

func resourceLocalFileRead(rd *schema.ResourceData, _ interface{}) error {
	config := newLocalFileCfg(rd)

  content, err := ioutil.ReadFile(config.Path)
  if err != nil {
    return err
  }

  rd.Set("content", string(content))

  return nil
}

func resourceLocalFileExists(rd *schema.ResourceData, _ interface{}) (bool, error) {
	config := newLocalFileCfg(rd)

	_, err := os.Stat(config.Path)
	if err != nil {
		if os.IsNotExist(err) {
			return false, nil
		} else {
			return false, err
		}
	} else {
		return true, nil
	}
}
<commit_msg>resource_localfile: Fix formatting for #2<commit_after>package localfile

import (
	"github.com/hashicorp/terraform/helper/schema"
	"os"
	"io/ioutil"
	"path/filepath"
	"fmt"
)

func resourceLocalFile() *schema.Resource {
	return &schema.Resource{
		Create: resourceLocalFileCreate,
		Read: resourceLocalFileRead,
		Update: nil,
		Delete: resourceLocalFileDelete,
		Schema: map[string]*schema.Schema{
			"path": &schema.Schema{
				Type: schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"content": &schema.Schema{
				Type: schema.TypeString,
				Required: true,
				ForceNew: true,
			},
		},
		Exists: resourceLocalFileExists,
	}
}

type localfileCfg struct {
	Path string
	Content string
}

func newLocalFileCfg(rd *schema.ResourceData) localfileCfg {
	return localfileCfg{
		Path: rd.Get("path").(string),
		Content: rd.Get("content").(string),
	}
}

func resourceLocalFileCreate(rd *schema.ResourceData, _ interface{}) error {
	config := newLocalFileCfg(rd)
	basedir := filepath.Dir(config.Path)

	err := os.MkdirAll(basedir, 0755)
	if err != nil {
		return err
	}

	f, err := os.Create(config.Path)
	if err != nil {
		return err
	}
	defer f.Close()
	rd.SetId(config.Path)

	_, err = f.WriteString(config.Content)
	f.Sync()
	return nil
}

func resourceLocalFileDelete(rd *schema.ResourceData, _ interface{}) error {
	config := newLocalFileCfg(rd)
	return os.Remove(config.Path)
}

func resourceLocalFileRead(rd *schema.ResourceData, _ interface{}) error {
	config := newLocalFileCfg(rd)

	content, err := ioutil.ReadFile(config.Path)
	if err != nil {
		return err
	}

	rd.Set("content", string(content))

	return nil
}

func resourceLocalFileExists(rd *schema.ResourceData, _ interface{}) (bool, error) {
	config := newLocalFileCfg(rd)

	_, err := os.Stat(config.Path)
	if err != nil {
		if os.IsNotExist(err) {
			return false, nil
		} else {
			return false, err
		}
	} else {
		return true, nil
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"testing"

	"bom"
)

func Test_addBom8(t *testing.T) {
	buf1 := make([]byte,0,16)
	in := bytes.NewReader([]byte{0x42})
	out := bytes.NewBuffer(buf1)

	addBom(in, out, bom.UTF8)
	if !bytes.Equal([]byte{0xef, 0xbb, 0xbf, 0x42}, out.Bytes()) {
		t.Errorf("invalid UTF8 bom data %v", out.Bytes())
	}
}

func Test_addBom8Again(t *testing.T) {
	buf1 := make([]byte,0,16)
	in := bytes.NewReader([]byte{0xef, 0xbb, 0xbf, 0x42})
	out := bytes.NewBuffer(buf1)

	addBom(in, out, bom.UTF8)
	if !bytes.Equal([]byte{0xef, 0xbb, 0xbf, 0x42}, out.Bytes()) {
		t.Errorf("extra BOM %v", out.Bytes())
	}
}

func Test_addBom32BE(t *testing.T) {
	buf1 := make([]byte,0,16)
	in := bytes.NewReader([]byte{0x42})
	out := bytes.NewBuffer(buf1)

	addBom(in, out, bom.UTF32BE)

	if !bytes.Equal([]byte{0x00, 0x00, 0xfe, 0xff,  0x42}, out.Bytes()) {
		t.Errorf("invalid UTF-32 BE bom data %v", out.Bytes())
	}
}

func Test_removeUTF16BE(t *testing.T) {
	in := bytes.NewReader([]byte{0xfe,0xff,0x42})
	out := bytes.NewBuffer(make([]byte,0,16))

	removeBom(in, out, bom.UTF16BE)
	if !bytes.Equal([]byte{0x42}, out.Bytes()) {
		t.Errorf("fail to remove UTF16 BE bom: ", out.Bytes())
	}
}

func Test_removeAnotherTargetBom(t *testing.T) {
	in := bytes.NewReader([]byte{0xfe,0xff,0x42,0x43})
	out := bytes.NewBuffer(make([]byte,0,16))

	removeBom(in, out, bom.UTF32LE)
	if !bytes.Equal([]byte{0xfe,0xff,0x42,0x43}, out.Bytes()) {
		t.Errorf("fail to remove UTF16 BE bom: ", out.Bytes())
	}
}

func Test_removeFromNoBomString(t *testing.T) {
	buf1 := make([]byte,0,16)
	in := bytes.NewReader([]byte{0x42,0x43,0x44})

	out := bytes.NewBuffer(buf1)

	removeBom(in, out, bom.UTF8)
	if !bytes.Equal([]byte{0x42,0x43,0x44}, out.Bytes()) {
		t.Errorf("fail to remove UTF16 BE bom: ", out.Bytes())
	}
}

<commit_msg>go fmt<commit_after>package main

import (
	"bytes"
	"testing"

	"bom"
)

func Test_addBom8(t *testing.T) {
	buf1 := make([]byte, 0, 16)
	in := bytes.NewReader([]byte{0x42})
	out := bytes.NewBuffer(buf1)

	addBom(in, out, bom.UTF8)
	if !bytes.Equal([]byte{0xef, 0xbb, 0xbf, 0x42}, out.Bytes()) {
		t.Errorf("invalid UTF8 bom data %v", out.Bytes())
	}
}

func Test_addBom8Again(t *testing.T) {
	buf1 := make([]byte, 0, 16)
	in := bytes.NewReader([]byte{0xef, 0xbb, 0xbf, 0x42})
	out := bytes.NewBuffer(buf1)

	addBom(in, out, bom.UTF8)
	if !bytes.Equal([]byte{0xef, 0xbb, 0xbf, 0x42}, out.Bytes()) {
		t.Errorf("extra BOM %v", out.Bytes())
	}
}

func Test_addBom32BE(t *testing.T) {
	buf1 := make([]byte, 0, 16)
	in := bytes.NewReader([]byte{0x42})
	out := bytes.NewBuffer(buf1)

	addBom(in, out, bom.UTF32BE)

	if !bytes.Equal([]byte{0x00, 0x00, 0xfe, 0xff, 0x42}, out.Bytes()) {
		t.Errorf("invalid UTF-32 BE bom data %v", out.Bytes())
	}
}

func Test_removeUTF16BE(t *testing.T) {
	in := bytes.NewReader([]byte{0xfe, 0xff, 0x42})
	out := bytes.NewBuffer(make([]byte, 0, 16))

	removeBom(in, out, bom.UTF16BE)
	if !bytes.Equal([]byte{0x42}, out.Bytes()) {
		t.Errorf("fail to remove UTF16 BE bom: ", out.Bytes())
	}
}

func Test_removeAnotherTargetBom(t *testing.T) {
	in := bytes.NewReader([]byte{0xfe, 0xff, 0x42, 0x43})
	out := bytes.NewBuffer(make([]byte, 0, 16))

	removeBom(in, out, bom.UTF32LE)
	if !bytes.Equal([]byte{0xfe, 0xff, 0x42, 0x43}, out.Bytes()) {
		t.Errorf("fail to remove UTF16 BE bom: ", out.Bytes())
	}
}

func Test_removeFromNoBomString(t *testing.T) {
	buf1 := make([]byte, 0, 16)
	in := bytes.NewReader([]byte{0x42, 0x43, 0x44})

	out := bytes.NewBuffer(buf1)

	removeBom(in, out, bom.UTF8)
	if !bytes.Equal([]byte{0x42, 0x43, 0x44}, out.Bytes()) {
		t.Errorf("fail to remove UTF16 BE bom: ", out.Bytes())
	}
}
<|endoftext|>


<commit_before>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build ignore

// The run program is invoked via the dist tool.
// To invoke manually: go tool dist test -run api --no-rebuild
package main

import (
	"errors"
	"fmt"
	exec "internal/execabs"
	"internal/goversion"
	"io/fs"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
)

func goCmd() string {
	var exeSuffix string
	if runtime.GOOS == "windows" {
		exeSuffix = ".exe"
	}
	path := filepath.Join(runtime.GOROOT(), "bin", "go"+exeSuffix)
	if _, err := os.Stat(path); err == nil {
		return path
	}
	return "go"
}

var goroot string

func main() {
	log.SetFlags(0)
	goroot = os.Getenv("GOROOT") // should be set by run.{bash,bat}
	if goroot == "" {
		log.Fatal("No $GOROOT set.")
	}
	if err := os.Chdir(filepath.Join(goroot, "api")); err != nil {
		log.Fatal(err)
	}

	files, err := filepath.Glob("go1*.txt")
	if err != nil {
		log.Fatal(err)
	}
	next, err := filepath.Glob(filepath.Join("next", "*.txt"))
	if err != nil {
		log.Fatal(err)
	}
	cmd := exec.Command(goCmd(), "tool", "api",
		"-c", strings.Join(files, ","),
		"-approval", strings.Join(append(approvalNeeded(files), next...), ","),
		allowNew(),
		"-next", strings.Join(next, ","),
		"-except", "except.txt",
	)
	fmt.Println(cmd.Args)
	out, err := cmd.CombinedOutput()
	if err != nil {
		log.Fatalf("Error running API checker: %v\n%s", err, out)
	}
	fmt.Print(string(out))
}

func approvalNeeded(files []string) []string {
	var out []string
	for _, f := range files {
		name := filepath.Base(f)
		if name == "go1.txt" {
			continue
		}
		minor := strings.TrimSuffix(strings.TrimPrefix(name, "go1."), ".txt")
		n, err := strconv.Atoi(minor)
		if err != nil {
			log.Fatalf("unexpected api file: %v", f)
		}
		if n >= 19 { // approvals started being tracked in Go 1.19
			out = append(out, f)
		}
	}
	return out
}

// allowNew returns the -allow_new flag to use for the 'go tool api' invocation.
func allowNew() string {
	// Experiment for Go 1.19: always require api file updates.
	return "-allow_new=false"

	// Verify that the api/go1.n.txt for previous Go version exists.
	// It definitely should, otherwise it's a signal that the logic below may be outdated.
	if _, err := os.Stat(fmt.Sprintf("go1.%d.txt", goversion.Version-1)); err != nil {
		log.Fatalln("Problem with api file for previous release:", err)
	}

	// See whether the api/go1.n.txt for this Go version has been created.
	// (As of April 2021, it gets created during the release of the first Beta.)
	_, err := os.Stat(fmt.Sprintf("go1.%d.txt", goversion.Version))
	if errors.Is(err, fs.ErrNotExist) {
		// It doesn't exist, so we're in development or before Beta 1.
		// At this stage, unmentioned API additions are deemed okay.
		// (They will be quietly shown in API check output, but the test won't fail).
		return "-allow_new=true"
	} else if err == nil {
		// The api/go1.n.txt for this Go version has been created,
		// so we're definitely past Beta 1 in the release cycle.
		//
		// From this point, enforce that api/go1.n.txt is an accurate and complete
		// representation of what's going into the release by failing API check if
		// there are API additions (a month into the freeze, there shouldn't be many).
		//
		// See golang.org/issue/43956.
		return "-allow_new=false"
	} else {
		log.Fatal(err)
	}
	panic("unreachable")
}
<commit_msg>cmd/api: remove debug print<commit_after>// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//go:build ignore

// The run program is invoked via the dist tool.
// To invoke manually: go tool dist test -run api --no-rebuild
package main

import (
	"errors"
	"fmt"
	exec "internal/execabs"
	"internal/goversion"
	"io/fs"
	"log"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
)

func goCmd() string {
	var exeSuffix string
	if runtime.GOOS == "windows" {
		exeSuffix = ".exe"
	}
	path := filepath.Join(runtime.GOROOT(), "bin", "go"+exeSuffix)
	if _, err := os.Stat(path); err == nil {
		return path
	}
	return "go"
}

var goroot string

func main() {
	log.SetFlags(0)
	goroot = os.Getenv("GOROOT") // should be set by run.{bash,bat}
	if goroot == "" {
		log.Fatal("No $GOROOT set.")
	}
	if err := os.Chdir(filepath.Join(goroot, "api")); err != nil {
		log.Fatal(err)
	}

	files, err := filepath.Glob("go1*.txt")
	if err != nil {
		log.Fatal(err)
	}
	next, err := filepath.Glob(filepath.Join("next", "*.txt"))
	if err != nil {
		log.Fatal(err)
	}
	cmd := exec.Command(goCmd(), "tool", "api",
		"-c", strings.Join(files, ","),
		"-approval", strings.Join(append(approvalNeeded(files), next...), ","),
		allowNew(),
		"-next", strings.Join(next, ","),
		"-except", "except.txt",
	)
	out, err := cmd.CombinedOutput()
	if err != nil {
		log.Fatalf("Error running API checker: %v\n%s", err, out)
	}
	fmt.Print(string(out))
}

func approvalNeeded(files []string) []string {
	var out []string
	for _, f := range files {
		name := filepath.Base(f)
		if name == "go1.txt" {
			continue
		}
		minor := strings.TrimSuffix(strings.TrimPrefix(name, "go1."), ".txt")
		n, err := strconv.Atoi(minor)
		if err != nil {
			log.Fatalf("unexpected api file: %v", f)
		}
		if n >= 19 { // approvals started being tracked in Go 1.19
			out = append(out, f)
		}
	}
	return out
}

// allowNew returns the -allow_new flag to use for the 'go tool api' invocation.
func allowNew() string {
	// Experiment for Go 1.19: always require api file updates.
	return "-allow_new=false"

	// Verify that the api/go1.n.txt for previous Go version exists.
	// It definitely should, otherwise it's a signal that the logic below may be outdated.
	if _, err := os.Stat(fmt.Sprintf("go1.%d.txt", goversion.Version-1)); err != nil {
		log.Fatalln("Problem with api file for previous release:", err)
	}

	// See whether the api/go1.n.txt for this Go version has been created.
	// (As of April 2021, it gets created during the release of the first Beta.)
	_, err := os.Stat(fmt.Sprintf("go1.%d.txt", goversion.Version))
	if errors.Is(err, fs.ErrNotExist) {
		// It doesn't exist, so we're in development or before Beta 1.
		// At this stage, unmentioned API additions are deemed okay.
		// (They will be quietly shown in API check output, but the test won't fail).
		return "-allow_new=true"
	} else if err == nil {
		// The api/go1.n.txt for this Go version has been created,
		// so we're definitely past Beta 1 in the release cycle.
		//
		// From this point, enforce that api/go1.n.txt is an accurate and complete
		// representation of what's going into the release by failing API check if
		// there are API additions (a month into the freeze, there shouldn't be many).
		//
		// See golang.org/issue/43956.
		return "-allow_new=false"
	} else {
		log.Fatal(err)
	}
	panic("unreachable")
}
<|endoftext|>