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

package secrets

import (
	"context"
	"fmt"
	"math/rand"
	"strings"
	"sync"
	"testing"
	"time"

	gax "github.com/googleapis/gax-go/v2"
	secretmanagerpb "google.golang.org/genproto/googleapis/cloud/secretmanager/v1"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"

	"go.chromium.org/luci/common/clock"
	"go.chromium.org/luci/common/clock/testclock"
	"go.chromium.org/luci/common/data/rand/mathrand"
	"go.chromium.org/luci/common/logging"
	"go.chromium.org/luci/common/logging/gologger"

	. "github.com/smartystreets/goconvey/convey"
)

func TestSecretManagerSource(t *testing.T) {
	t.Parallel()

	Convey("With SecretManagerStore", t, func() {
		ctx := context.Background()
		ctx = gologger.StdConfig.Use(ctx)
		ctx = logging.SetLevel(ctx, logging.Debug)
		ctx = mathrand.Set(ctx, rand.New(rand.NewSource(123)))
		ctx, tc := testclock.UseTime(ctx, testclock.TestRecentTimeUTC)
		ctx, cancel := context.WithCancel(ctx)
		defer cancel()

		// Machinery to advance fake time in a lock step with MaintenanceLoop.
		ticks := make(chan bool)
		tc.SetTimerCallback(func(d time.Duration, _ clock.Timer) {
			// Do not block clock.After call itself, just delay advancing of the
			// clock until `ticks` is signaled.
			go func() {
				select {
				case do := <-ticks:
					if do {
						tc.Add(d)
					}
				case <-ctx.Done():
				}
			}()
		})

		// Handle reports from MaintenanceLoop.
		events := make(chan string)
		expectChecked := func(secret string) {
			So(<-events, ShouldEqual, "checking")
			So(<-events, ShouldEqual, "checked "+secret)
		}
		expectReloaded := func(secret string) {
			So(<-events, ShouldEqual, "checking")
			So(<-events, ShouldEqual, "reloaded "+secret)
		}
		expectSleep := func(dur string) {
			So(<-events, ShouldEqual, "sleeping")
			ticks <- true // advance clock in the timer
			So(<-events, ShouldEqual, "slept "+dur)
		}
		expectWoken := func(afterSleep bool) {
			So(<-events, ShouldEqual, "sleeping")
			So(<-events, ShouldEqual, "woken")
			if afterSleep {
				ticks <- false // the timer was aborted, don't advance clock in it
			}
			So(<-events, ShouldEqual, "checking")
		}

		gsm := secretManagerMock{}

		sm := SecretManagerStore{
			CloudProject:        "fake-project",
			AccessSecretVersion: gsm.AccessSecretVersion,
			testingEvents:       events,
		}
		go sm.MaintenanceLoop(ctx)
		So(<-events, ShouldEqual, "checking") // wait until it blocks

		Convey("normalizeName sm://<secret>", func() {
			name, err := sm.normalizeName("sm://secret")
			So(err, ShouldBeNil)
			So(name, ShouldEqual, "sm://fake-project/secret")
		})

		Convey("readSecret devsecret", func() {
			s, err := sm.readSecret(ctx, "devsecret://YWJj", false)
			So(err, ShouldBeNil)
			So(s, ShouldResemble, &trackedSecret{
				name: "devsecret://YWJj",
				value: Secret{
					Current: []byte("abc"),
				},
			})
		})

		Convey("readSecret devsecret-text", func() {
			s, err := sm.readSecret(ctx, "devsecret-text://abc", false)
			So(err, ShouldBeNil)
			So(s, ShouldResemble, &trackedSecret{
				name: "devsecret-text://abc",
				value: Secret{
					Current: []byte("abc"),
				},
			})
		})

		Convey("readSecret sm://<project>/<secret>", func() {
			gsm.createVersion("project", "secret", "zzz")

			s, err := sm.readSecret(ctx, "sm://project/secret", false)
			So(err, ShouldBeNil)
			So(s.name, ShouldEqual, "sm://project/secret")
			So(s.value, ShouldResemble, Secret{Current: []byte("zzz")})
			So(s.versions, ShouldEqual, [2]int64{1, 0})
			So(s.nextReload.IsZero(), ShouldBeFalse)
		})

		Convey("readSecret with prev version", func() {
			gsm.createVersion("project", "secret", "old")
			gsm.createVersion("project", "secret", "new")

			s, err := sm.readSecret(ctx, "sm://project/secret", true)
			So(err, ShouldBeNil)
			So(s.value, ShouldResemble, Secret{
				Current: []byte("new"),
				Previous: [][]byte{
					[]byte("old"),
				},
			})
			So(s.versions, ShouldEqual, [2]int64{2, 1})
		})

		Convey("readSecret with prev version disabled", func() {
			ref := gsm.createVersion("project", "secret", "old")
			gsm.createVersion("project", "secret", "new")
			gsm.disableVersion(ref)

			s, err := sm.readSecret(ctx, "sm://project/secret", true)
			So(err, ShouldBeNil)
			So(s.value, ShouldResemble, Secret{Current: []byte("new")})
			So(s.versions, ShouldEqual, [2]int64{2, 0})
		})

		Convey("readSecret with prev version deleted", func() {
			ref := gsm.createVersion("project", "secret", "old")
			gsm.createVersion("project", "secret", "new")
			gsm.deleteVersion(ref)

			s, err := sm.readSecret(ctx, "sm://project/secret", true)
			So(err, ShouldBeNil)
			So(s.value, ShouldResemble, Secret{Current: []byte("new")})
			So(s.versions, ShouldEqual, [2]int64{2, 0})
		})

		Convey("Derived secrets work", func() {
			var (
				ver1 = []byte{56, 113, 147, 97, 153, 240, 138, 213, 51, 101, 163, 53, 195, 45, 143, 253}
				ver2 = []byte{246, 39, 65, 71, 93, 43, 95, 59, 139, 134, 84, 40, 226, 44, 2, 47}
			)

			gsm.createVersion("project", "secret", "zzz")

			So(sm.LoadRootSecret(ctx, "sm://project/secret"), ShouldBeNil)
			expectWoken(false)

			s1, err := sm.RandomSecret(ctx, "name")
			So(err, ShouldBeNil)
			So(s1, ShouldResemble, Secret{Current: ver1})

			rotated := make(chan struct{})
			sm.AddRotationHandler(ctx, "sm://project/secret", func(_ context.Context, s Secret) {
				close(rotated)
			})

			// Rotate the secret and make sure the change is picked up.
			gsm.createVersion("project", "secret", "xxx")
			expectSleep("2h16m23s")
			expectReloaded("sm://project/secret")
			<-rotated

			// Random secrets are rotated too.
			s2, err := sm.RandomSecret(ctx, "name")
			So(err, ShouldBeNil)
			So(s2, ShouldResemble, Secret{
				Current:  ver2,
				Previous: [][]byte{ver1},
			})
		})

		Convey("Stored secrets OK", func() {
			gsm.createVersion("project", "secret", "v1")

			rotated := make(chan struct{})
			sm.AddRotationHandler(ctx, "sm://project/secret", func(_ context.Context, s Secret) {
				rotated <- struct{}{}
			})

			s, err := sm.StoredSecret(ctx, "sm://project/secret")
			So(err, ShouldBeNil)
			So(s, ShouldResemble, Secret{Current: []byte("v1")})
			expectWoken(false)

			// Rotate the secret and make sure the change is picked up when expected.
			gsm.createVersion("project", "secret", "v2")
			expectSleep("2h16m23s")
			expectReloaded("sm://project/secret")

			s, err = sm.StoredSecret(ctx, "sm://project/secret")
			So(err, ShouldBeNil)
			So(s, ShouldResemble, Secret{Current: []byte("v2")})

			<-rotated // doesn't hang
		})

		Convey("Stored secrets exponential back-off", func() {
			gsm.createVersion("project", "secret", "v1")

			s, err := sm.StoredSecret(ctx, "sm://project/secret")
			So(err, ShouldBeNil)
			So(s, ShouldResemble, Secret{Current: []byte("v1")})
			expectWoken(false)

			// Rotate the secret and "break" the backend.
			gsm.createVersion("project", "secret", "v2")
			gsm.setError(status.Errorf(codes.Internal, "boom"))

			// Attempts to do a regular update first.
			expectSleep("2h16m23s")
			expectChecked("sm://project/secret")

			// Notices the error and starts checking more often.
			expectSleep("2s")
			expectChecked("sm://project/secret")
			expectSleep("6s")
			expectChecked("sm://project/secret")

			// "Fix" the backend.
			gsm.setError(nil)

			// The updated eventually succeeds and returns to the slow schedule.
			expectSleep("14s")
			expectReloaded("sm://project/secret")
			expectSleep("2h17m45s")
			expectChecked("sm://project/secret")
		})

		Convey("Stored secrets priority queue", func() {
			gsm.createVersion("project", "secret1", "v1")
			gsm.createVersion("project", "secret2", "v1")

			// Load the first one and let it be for a while to advance time.
			sm.StoredSecret(ctx, "sm://project/secret1")
			expectWoken(false)
			expectSleep("2h16m23s")
			expectChecked("sm://project/secret1")

			// Load the second one, it wakes up the maintenance loop, but it finds
			// there's nothing to reload yet.
			sm.StoredSecret(ctx, "sm://project/secret2")
			expectWoken(true)

			// Starts waking up periodically to update one secret or another. Checks
			// for secret1 and secret2 happened to be bunched relatively close to
			// one another (~7m).
			expectSleep("3h47m31s")
			expectChecked("sm://project/secret2")
			expectSleep("6m39s")
			expectChecked("sm://project/secret1")
			expectSleep("2h17m45s")
			expectChecked("sm://project/secret1")
			expectSleep("12m48s")
			expectChecked("sm://project/secret2")
		})
	})
}

type secretManagerMock struct {
	m        sync.Mutex
	err      error
	versions map[string]string // full ref => a value or "" if disabled
	latest   map[string]int    // latest ref => version number
}

func (sm *secretManagerMock) createVersion(project, name, value string) string {
	sm.m.Lock()
	defer sm.m.Unlock()

	if sm.versions == nil {
		sm.versions = make(map[string]string, 1)
		sm.latest = make(map[string]int, 1)
	}

	latestRef := fmt.Sprintf("projects/%s/secrets/%s/versions/latest", project, name)

	nextVer := sm.latest[latestRef] + 1
	versionRef := fmt.Sprintf("projects/%s/secrets/%s/versions/%d", project, name, nextVer)

	sm.versions[versionRef] = value
	sm.latest[latestRef] = nextVer

	return versionRef
}

func (sm *secretManagerMock) disableVersion(ref string) {
	sm.m.Lock()
	defer sm.m.Unlock()

	sm.versions[ref] = ""
}

func (sm *secretManagerMock) deleteVersion(ref string) {
	sm.m.Lock()
	defer sm.m.Unlock()

	delete(sm.versions, ref)
}

func (sm *secretManagerMock) setError(err error) {
	sm.m.Lock()
	defer sm.m.Unlock()
	sm.err = err
}

func (sm *secretManagerMock) AccessSecretVersion(_ context.Context, req *secretmanagerpb.AccessSecretVersionRequest, _ ...gax.CallOption) (*secretmanagerpb.AccessSecretVersionResponse, error) {
	sm.m.Lock()
	defer sm.m.Unlock()

	if sm.err != nil {
		return nil, sm.err
	}

	// Recognize ".../versions/latest" aliases.
	versionRef := req.Name
	if ver := sm.latest[versionRef]; ver != 0 {
		versionRef = strings.ReplaceAll(versionRef, "/versions/latest", fmt.Sprintf("/versions/%d", ver))
	}

	switch val, ok := sm.versions[versionRef]; {
	case !ok:
		return nil, status.Errorf(codes.NotFound, "no such version")
	case val == "":
		return nil, status.Errorf(codes.FailedPrecondition, "the version is disabled")
	default:
		return &secretmanagerpb.AccessSecretVersionResponse{
			Name:    versionRef,
			Payload: &secretmanagerpb.SecretPayload{Data: []byte(val)},
		}, nil
	}
}
<commit_msg>[secrets] Another attempt at fixing flaky test.<commit_after>// Copyright 2020 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package secrets

import (
	"context"
	"fmt"
	"math/rand"
	"strings"
	"sync"
	"testing"
	"time"

	gax "github.com/googleapis/gax-go/v2"
	secretmanagerpb "google.golang.org/genproto/googleapis/cloud/secretmanager/v1"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"

	"go.chromium.org/luci/common/clock"
	"go.chromium.org/luci/common/clock/testclock"
	"go.chromium.org/luci/common/data/rand/mathrand"
	"go.chromium.org/luci/common/logging"
	"go.chromium.org/luci/common/logging/gologger"

	. "github.com/smartystreets/goconvey/convey"
)

func TestSecretManagerSource(t *testing.T) {
	t.Parallel()

	Convey("With SecretManagerStore", t, func() {
		ctx := context.Background()
		ctx = gologger.StdConfig.Use(ctx)
		ctx = logging.SetLevel(ctx, logging.Debug)
		ctx = mathrand.Set(ctx, rand.New(rand.NewSource(123)))
		ctx, tc := testclock.UseTime(ctx, testclock.TestRecentTimeUTC)
		ctx, cancel := context.WithCancel(ctx)
		defer cancel()

		// Machinery to advance fake time in a lock step with MaintenanceLoop.
		ticks := make(chan bool)
		tc.SetTimerCallback(func(d time.Duration, _ clock.Timer) {
			// Do not block clock.After call itself, just delay advancing of the
			// clock until `ticks` is signaled.
			go func() {
				select {
				case do := <-ticks:
					if do {
						tc.Add(d)
					}
				case <-ctx.Done():
				}
			}()
		})

		// Handle reports from MaintenanceLoop.
		events := make(chan string)
		expectChecked := func(secret string) {
			So(<-events, ShouldEqual, "checking")
			So(<-events, ShouldEqual, "checked "+secret)
		}
		expectReloaded := func(secret string) {
			So(<-events, ShouldEqual, "checking")
			So(<-events, ShouldEqual, "reloaded "+secret)
		}
		expectSleeping := func() {
			So(<-events, ShouldEqual, "sleeping")
		}
		expectFullSleep := func(dur string) {
			expectSleeping()
			ticks <- true // advance clock in the timer
			So(<-events, ShouldEqual, "slept "+dur)
		}
		expectWoken := func(afterSleep bool) {
			So(<-events, ShouldEqual, "woken")
			if afterSleep {
				ticks <- false // the timer was aborted, don't advance clock in it
			}
			So(<-events, ShouldEqual, "checking")
		}

		gsm := secretManagerMock{}

		sm := SecretManagerStore{
			CloudProject:        "fake-project",
			AccessSecretVersion: gsm.AccessSecretVersion,
			testingEvents:       events,
		}
		go sm.MaintenanceLoop(ctx)
		So(<-events, ShouldEqual, "checking") // wait until it blocks

		Convey("normalizeName sm://<secret>", func() {
			name, err := sm.normalizeName("sm://secret")
			So(err, ShouldBeNil)
			So(name, ShouldEqual, "sm://fake-project/secret")
		})

		Convey("readSecret devsecret", func() {
			s, err := sm.readSecret(ctx, "devsecret://YWJj", false)
			So(err, ShouldBeNil)
			So(s, ShouldResemble, &trackedSecret{
				name: "devsecret://YWJj",
				value: Secret{
					Current: []byte("abc"),
				},
			})
		})

		Convey("readSecret devsecret-text", func() {
			s, err := sm.readSecret(ctx, "devsecret-text://abc", false)
			So(err, ShouldBeNil)
			So(s, ShouldResemble, &trackedSecret{
				name: "devsecret-text://abc",
				value: Secret{
					Current: []byte("abc"),
				},
			})
		})

		Convey("readSecret sm://<project>/<secret>", func() {
			gsm.createVersion("project", "secret", "zzz")

			s, err := sm.readSecret(ctx, "sm://project/secret", false)
			So(err, ShouldBeNil)
			So(s.name, ShouldEqual, "sm://project/secret")
			So(s.value, ShouldResemble, Secret{Current: []byte("zzz")})
			So(s.versions, ShouldEqual, [2]int64{1, 0})
			So(s.nextReload.IsZero(), ShouldBeFalse)
		})

		Convey("readSecret with prev version", func() {
			gsm.createVersion("project", "secret", "old")
			gsm.createVersion("project", "secret", "new")

			s, err := sm.readSecret(ctx, "sm://project/secret", true)
			So(err, ShouldBeNil)
			So(s.value, ShouldResemble, Secret{
				Current: []byte("new"),
				Previous: [][]byte{
					[]byte("old"),
				},
			})
			So(s.versions, ShouldEqual, [2]int64{2, 1})
		})

		Convey("readSecret with prev version disabled", func() {
			ref := gsm.createVersion("project", "secret", "old")
			gsm.createVersion("project", "secret", "new")
			gsm.disableVersion(ref)

			s, err := sm.readSecret(ctx, "sm://project/secret", true)
			So(err, ShouldBeNil)
			So(s.value, ShouldResemble, Secret{Current: []byte("new")})
			So(s.versions, ShouldEqual, [2]int64{2, 0})
		})

		Convey("readSecret with prev version deleted", func() {
			ref := gsm.createVersion("project", "secret", "old")
			gsm.createVersion("project", "secret", "new")
			gsm.deleteVersion(ref)

			s, err := sm.readSecret(ctx, "sm://project/secret", true)
			So(err, ShouldBeNil)
			So(s.value, ShouldResemble, Secret{Current: []byte("new")})
			So(s.versions, ShouldEqual, [2]int64{2, 0})
		})

		Convey("Derived secrets work", func() {
			var (
				ver1 = []byte{56, 113, 147, 97, 153, 240, 138, 213, 51, 101, 163, 53, 195, 45, 143, 253}
				ver2 = []byte{246, 39, 65, 71, 93, 43, 95, 59, 139, 134, 84, 40, 226, 44, 2, 47}
			)

			gsm.createVersion("project", "secret", "zzz")

			expectSleeping()
			So(sm.LoadRootSecret(ctx, "sm://project/secret"), ShouldBeNil)
			expectWoken(false)

			s1, err := sm.RandomSecret(ctx, "name")
			So(err, ShouldBeNil)
			So(s1, ShouldResemble, Secret{Current: ver1})

			rotated := make(chan struct{})
			sm.AddRotationHandler(ctx, "sm://project/secret", func(_ context.Context, s Secret) {
				close(rotated)
			})

			// Rotate the secret and make sure the change is picked up.
			gsm.createVersion("project", "secret", "xxx")
			expectFullSleep("2h16m23s")
			expectReloaded("sm://project/secret")
			<-rotated

			// Random secrets are rotated too.
			s2, err := sm.RandomSecret(ctx, "name")
			So(err, ShouldBeNil)
			So(s2, ShouldResemble, Secret{
				Current:  ver2,
				Previous: [][]byte{ver1},
			})
		})

		Convey("Stored secrets OK", func() {
			gsm.createVersion("project", "secret", "v1")

			rotated := make(chan struct{})
			sm.AddRotationHandler(ctx, "sm://project/secret", func(_ context.Context, s Secret) {
				rotated <- struct{}{}
			})

			expectSleeping()
			s, err := sm.StoredSecret(ctx, "sm://project/secret")
			So(err, ShouldBeNil)
			So(s, ShouldResemble, Secret{Current: []byte("v1")})
			expectWoken(false)

			// Rotate the secret and make sure the change is picked up when expected.
			gsm.createVersion("project", "secret", "v2")
			expectFullSleep("2h16m23s")
			expectReloaded("sm://project/secret")

			s, err = sm.StoredSecret(ctx, "sm://project/secret")
			So(err, ShouldBeNil)
			So(s, ShouldResemble, Secret{Current: []byte("v2")})

			<-rotated // doesn't hang
		})

		Convey("Stored secrets exponential back-off", func() {
			gsm.createVersion("project", "secret", "v1")

			expectSleeping()
			s, err := sm.StoredSecret(ctx, "sm://project/secret")
			So(err, ShouldBeNil)
			So(s, ShouldResemble, Secret{Current: []byte("v1")})
			expectWoken(false)

			// Rotate the secret and "break" the backend.
			gsm.createVersion("project", "secret", "v2")
			gsm.setError(status.Errorf(codes.Internal, "boom"))

			// Attempts to do a regular update first.
			expectFullSleep("2h16m23s")
			expectChecked("sm://project/secret")

			// Notices the error and starts checking more often.
			expectFullSleep("2s")
			expectChecked("sm://project/secret")
			expectFullSleep("6s")
			expectChecked("sm://project/secret")

			// "Fix" the backend.
			gsm.setError(nil)

			// The updated eventually succeeds and returns to the slow schedule.
			expectFullSleep("14s")
			expectReloaded("sm://project/secret")
			expectFullSleep("2h17m45s")
			expectChecked("sm://project/secret")
		})

		Convey("Stored secrets priority queue", func() {
			gsm.createVersion("project", "secret1", "v1")
			gsm.createVersion("project", "secret2", "v1")

			// Load the first one and let it be for a while to advance time.
			expectSleeping()
			sm.StoredSecret(ctx, "sm://project/secret1")
			expectWoken(false)
			expectFullSleep("2h16m23s")
			expectChecked("sm://project/secret1")

			// Load the second one, it wakes up the maintenance loop, but it finds
			// there's nothing to reload yet.
			expectSleeping()
			sm.StoredSecret(ctx, "sm://project/secret2")
			expectWoken(true)

			// Starts waking up periodically to update one secret or another. Checks
			// for secret1 and secret2 happened to be bunched relatively close to
			// one another (~7m).
			expectFullSleep("3h47m31s")
			expectChecked("sm://project/secret2")
			expectFullSleep("6m39s")
			expectChecked("sm://project/secret1")
			expectFullSleep("2h17m45s")
			expectChecked("sm://project/secret1")
			expectFullSleep("12m48s")
			expectChecked("sm://project/secret2")
		})
	})
}

type secretManagerMock struct {
	m        sync.Mutex
	err      error
	versions map[string]string // full ref => a value or "" if disabled
	latest   map[string]int    // latest ref => version number
}

func (sm *secretManagerMock) createVersion(project, name, value string) string {
	sm.m.Lock()
	defer sm.m.Unlock()

	if sm.versions == nil {
		sm.versions = make(map[string]string, 1)
		sm.latest = make(map[string]int, 1)
	}

	latestRef := fmt.Sprintf("projects/%s/secrets/%s/versions/latest", project, name)

	nextVer := sm.latest[latestRef] + 1
	versionRef := fmt.Sprintf("projects/%s/secrets/%s/versions/%d", project, name, nextVer)

	sm.versions[versionRef] = value
	sm.latest[latestRef] = nextVer

	return versionRef
}

func (sm *secretManagerMock) disableVersion(ref string) {
	sm.m.Lock()
	defer sm.m.Unlock()

	sm.versions[ref] = ""
}

func (sm *secretManagerMock) deleteVersion(ref string) {
	sm.m.Lock()
	defer sm.m.Unlock()

	delete(sm.versions, ref)
}

func (sm *secretManagerMock) setError(err error) {
	sm.m.Lock()
	defer sm.m.Unlock()
	sm.err = err
}

func (sm *secretManagerMock) AccessSecretVersion(_ context.Context, req *secretmanagerpb.AccessSecretVersionRequest, _ ...gax.CallOption) (*secretmanagerpb.AccessSecretVersionResponse, error) {
	sm.m.Lock()
	defer sm.m.Unlock()

	if sm.err != nil {
		return nil, sm.err
	}

	// Recognize ".../versions/latest" aliases.
	versionRef := req.Name
	if ver := sm.latest[versionRef]; ver != 0 {
		versionRef = strings.ReplaceAll(versionRef, "/versions/latest", fmt.Sprintf("/versions/%d", ver))
	}

	switch val, ok := sm.versions[versionRef]; {
	case !ok:
		return nil, status.Errorf(codes.NotFound, "no such version")
	case val == "":
		return nil, status.Errorf(codes.FailedPrecondition, "the version is disabled")
	default:
		return &secretmanagerpb.AccessSecretVersionResponse{
			Name:    versionRef,
			Payload: &secretmanagerpb.SecretPayload{Data: []byte(val)},
		}, nil
	}
}
<|endoftext|>


<commit_before>// +build noos

package syscall

import (
	"internal"
	"sync/atomic"
	"unsafe"
)

// Event represents an event that multiple tasks or ISRs can send and multiple
// tasks (but not ISRs) can wait for. Events are intended for use by low-level
// library rutines to implement higher level communication and synchronization
// primitives like channels and mutexes. They are specific to noos runtime so
// can be unavailable if RTOS is used.
type Event uintptr

const eventBits = uint32(unsafe.Sizeof(Event(0)) * 8)

var (
	eventReg Event
	gen      uint32
)

// AssignEvent returns event from some internal event pool.
// There is no any guarantee that subsequent calls of AssignEvent assigns
// different events, which means that AssignEvent can return Event already
// assigned by current or another task.
func AssignEvent() Event {
	u := atomic.AddUint32(&gen, 1)
	return Event(1) << (u & (eventBits - 1))
}

// AssignEventFlag works like AssignEvent but guarantees that the least
// significant bit of returned value is zero.
func AssignEventFlag() Event {
	u := atomic.AddUint32(&gen, 1)
	return Event(2) << (u % (eventBits - 1))
}

// Send sends event that means it waking up all tasks that wait for e.
// If some task isn't waiting for any event, e is saved for this task
// for possible future call of Wait. Compiler doesn't reorder Send with any
// memory operation that is before it in the program code.
func (e Event) Send() {
	atomic.OrUintptr((*uintptr)(&eventReg), uintptr(e))
	schedNext()
}

// Sum returns logical sum of events.
// Sending the sum of events is equal to send all that events at once. Waiting
// for sum of events means waiting for at least one event from sum.
func (e Event) Sum(a Event) Event {
	return e | a
}

// TakeEventReg is intended to be used by runtime to obtain accumulated events.
// It returns value of shared event register and clears it in one atomic
// operation.
func TakeEventReg() Event {
	return Event(atomic.SwapUintptr((*uintptr)(&eventReg), 0))
}

// Wait waits for event.
// If e == 0 it returns immediately. Wait clears all saved events for current
// task so the information about sended events, that Wait hasn't waited for,
// is lost. Compiler doesn't reorder Wait with any memory operation that is
// before or after it in the program code.
func (e Event) Wait() {
	internal.Syscall1(EVENTWAIT, uintptr(e))
}

// Alarm is an event that is sent by runtime when asked by using SetAlarm.
var Alarm = AssignEventFlag()

func AtomicLoadEvent(p *Event) Event {
	return Event(atomic.LoadUintptr((*uintptr)(p)))
}

func AtomicStoreEvent(p *Event, e Event) {
	atomic.StoreUintptr((*uintptr)(p), uintptr(e))
}

func AtomicAddEvent(p *Event, delta int) Event {
	return Event(atomic.AddUintptr((*uintptr)(p), 1))
}

func AtomicCompareAndSwapEvent(p *Event, old, new Event) bool {
	return atomic.CompareAndSwapUintptr(
		(*uintptr)(p), uintptr(old), uintptr(new),
	)
}
<commit_msg>syscall: Use consistently uintptr as Event. Fix bug in AtomicAddEvent method.<commit_after>// +build noos

package syscall

import (
	"internal"
	"sync/atomic"
	"unsafe"
)

// Event bitmask that represents events that multiple tasks or ISRs can send
// and multiple tasks (but not ISRs) can wait for. Events are intended to be
// used by low-level library rutines to implement higher level communication and
// synchronization primitives like channels and mutexes. They are specific to
// noos runtime so can be unavailable if RTOS is used.
type Event uintptr

const eventBits = uintptr(unsafe.Sizeof(Event(0)) * 8)

var (
	eventReg Event
	gen      uintptr
)

// AssignEvent returns event from some internal event pool.
// There is no any guarantee that subsequent calls of AssignEvent assigns
// different events, which means that AssignEvent can return Event already
// assigned by current or another task.
func AssignEvent() Event {
	u := atomic.AddUintptr(&gen, 1)
	return Event(1) << (u & (eventBits - 1))
}

// AssignEventFlag works like AssignEvent but guarantees that the least
// significant bit of returned value is zero.
func AssignEventFlag() Event {
	u := atomic.AddUintptr(&gen, 1)
	return Event(2) << (u % (eventBits - 1))
}

// Send sends event that means it waking up all tasks that wait for e.
// If some task isn't waiting for any event, e is saved for this task
// for possible future call of Wait. Compiler doesn't reorder Send with any
// memory operation that is before it in the program code.
func (e Event) Send() {
	atomic.OrUintptr((*uintptr)(&eventReg), uintptr(e))
	schedNext()
}

// TakeEventReg is intended to be used by runtime to obtain accumulated events.
// It returns value of shared event register and clears it in one atomic
// operation.
func TakeEventReg() Event {
	return Event(atomic.SwapUintptr((*uintptr)(&eventReg), 0))
}

// Wait waits for event.
// If e == 0 it returns immediately. Wait clears all saved events for current
// task so the information about sended events, that Wait hasn't waited for,
// is lost. Compiler doesn't reorder Wait with any memory operation that is
// before or after it in the program code.
func (e Event) Wait() {
	internal.Syscall1(EVENTWAIT, uintptr(e))
}

// Alarm is an event that is sent by runtime when asked by using SetAlarm.
var Alarm = AssignEventFlag()

func AtomicLoadEvent(p *Event) Event {
	return Event(atomic.LoadUintptr((*uintptr)(p)))
}

func AtomicStoreEvent(p *Event, e Event) {
	atomic.StoreUintptr((*uintptr)(p), uintptr(e))
}

func AtomicAddEvent(p *Event, delta int) Event {
	return Event(atomic.AddUintptr((*uintptr)(p), uintptr(delta)))
}

func AtomicCompareAndSwapEvent(p *Event, old, new Event) bool {
	return atomic.CompareAndSwapUintptr(
		(*uintptr)(p), uintptr(old), uintptr(new),
	)
}
<|endoftext|>


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

package metrics

import (
	"fmt"
	"math"
	"net/http"
	"runtime"
	"sort"
)

var prefix = ""

func SetPrefix(p string) {
	prefix = p
}

var memstats = new(runtime.MemStats)

func init() {
	http.Handle("/metrics", http.HandlerFunc(printMetrics))
}

func printMetrics(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")

	// Runtime memory stats
	runtime.ReadMemStats(memstats)
	// General statistics.
	fmt.Fprintf(w, "%smem_alloc %d\n", prefix, memstats.Alloc)            // bytes allocated and not yet freed
	fmt.Fprintf(w, "%smem_alloc_total %d\n", prefix, memstats.TotalAlloc) // bytes allocated (even if freed)
	fmt.Fprintf(w, "%smem_sys %d\n", prefix, memstats.Sys)                // bytes obtained from system (sum of XxxSys below)
	fmt.Fprintf(w, "%smem_ptr_lookups %d\n", prefix, memstats.Lookups)    // number of pointer lookups
	fmt.Fprintf(w, "%smem_mallocs %d\n", prefix, memstats.Mallocs)        // number of mallocs
	fmt.Fprintf(w, "%smem_frees %d\n", prefix, memstats.Frees)            // number of frees

	// Main allocation heap statistics.
	fmt.Fprintf(w, "%smem_heap_alloc %d\n", prefix, memstats.HeapAlloc)       // bytes allocated and not yet freed (same as Alloc above)
	fmt.Fprintf(w, "%smem_heap_sys %d\n", prefix, memstats.HeapSys)           // bytes obtained from system
	fmt.Fprintf(w, "%smem_heap_idle %d\n", prefix, memstats.HeapIdle)         // bytes in idle spans
	fmt.Fprintf(w, "%smem_heap_in_use %d\n", prefix, memstats.HeapInuse)      // bytes in non-idle span
	fmt.Fprintf(w, "%smem_heap_released %d\n", prefix, memstats.HeapReleased) // bytes released to the OS
	fmt.Fprintf(w, "%smem_heap_objects %d\n", prefix, memstats.HeapObjects)   // total number of allocated objects

	fmt.Fprintf(w, "%smem_stack_in_use %d\n", prefix, memstats.StackInuse) // bytes used by stack allocator
	fmt.Fprintf(w, "%smem_stack_sys %d\n", prefix, memstats.StackSys)
	fmt.Fprintf(w, "%smem_mspan_in_use %d\n", prefix, memstats.MSpanInuse) // mspan structures
	fmt.Fprintf(w, "%smem_mspan_sys %d\n", prefix, memstats.MSpanSys)
	fmt.Fprintf(w, "%smem_mcache_in_use %d\n", prefix, memstats.MCacheInuse) // mcache structures
	fmt.Fprintf(w, "%smem_mcache_sys %d\n", prefix, memstats.MCacheSys)
	fmt.Fprintf(w, "%smem_buck_hash_sys %d\n", prefix, memstats.BuckHashSys) // profiling bucket hash table
	fmt.Fprintf(w, "%smem_gc_sys %d\n", prefix, memstats.GCSys)              // GC metadata
	fmt.Fprintf(w, "%smem_other_sys %d\n", prefix, memstats.OtherSys)        // other system allocations

	fmt.Fprintf(w, "%sgc_next_gc_heap_alloc %d\n", prefix, memstats.NextGC) // next collection will happen when HeapAlloc ≥ this amount
	fmt.Fprintf(w, "%sgc_last_gc_time %d\n", prefix, memstats.LastGC)       // end time of last collection (nanoseconds since 1970)
	fmt.Fprintf(w, "%sgc_pause_total %d\n", prefix, memstats.PauseTotalNs)
	fmt.Fprintf(w, "%sgc_num_gc %d\n", prefix, memstats.NumGC)
	fmt.Fprintf(w, "%sgc_gc_cpu_frac %f\n", prefix, memstats.GCCPUFraction)

	// circular buffer of recent GC pause durations, most recent at [(NumGC+255)%256]
	pctls := pausePercentiles(memstats.PauseNs[:], memstats.NumGC)
	for i := 0; i < 20; i++ {
		p := pctls[i]
		fmt.Fprintf(w, "%sgc_pause_pctl_%d %d\n", prefix, i*5, p)
	}
	fmt.Fprintf(w, "%sgc_pause_pctl_%d %d\n", prefix, 99, pctls[20])
	fmt.Fprintf(w, "%sgc_pause_pctl_%d %d\n", prefix, 100, pctls[21])

	// Per-size allocation statistics.
	for _, b := range memstats.BySize {
		fmt.Fprintf(w, "%salloc_size_%d_mallocs %d\n", prefix, b.Size, b.Mallocs)
		fmt.Fprintf(w, "%salloc_size_%d_frees %d\n", prefix, b.Size, b.Frees)
	}

	// Histograms
	hists := getAllHistograms()
	for name, dat := range hists {
		fmt.Fprintf(w, "%shist_%s_count %d\n", prefix, name, dat.count)
		fmt.Fprintf(w, "%shist_%s_kept %d\n", prefix, name, dat.kept)

		if dat.total > 0 && dat.count > 0 {
			avg := float64(dat.total) / float64(dat.count)
			fmt.Fprintf(w, "%shist_%s_avg %f\n", prefix, name, avg)
		}

		pctls := hdatPercentiles(dat)
		if len(pctls) == 0 {
			continue
		}
		for i := 0; i < 20; i++ {
			p := pctls[i]
			fmt.Fprintf(w, "%shist_%s_pctl_%d %d\n", prefix, name, i*5, p)
		}
		fmt.Fprintf(w, "%shist_%s_pctl_%d %d\n", prefix, name, 99, pctls[20])
		fmt.Fprintf(w, "%shist_%s_pctl_%d %d\n", prefix, name, 100, pctls[21])
	}

	// Bucketized histograms
	// Buckets are based on the number of leading zeros in the number
	// so each is less than or equal to that number of ones. Therefore
	// bucket 0 means the number was greater than 0x7FFF_FFFF_FFFF_FFFF
	// and less than or equal to 0xFFFF_FFFF_FFFF_FFFF, which would be a huge
	// duration but it's a good example. As well, bucket 63 only hold values
	// 0x0 and 0x1. Bucket 62 hold 0x10 and 0x11. 61: 0x100, 0x101, 0x110, 0x111
	bhists := getAllBucketHistograms()
	for name, bh := range bhists {
		var bmax uint64 = math.MaxUint64 // 0xFFFF_FFFF_FFFF_FFFF
		for i := 0; i < 64; i++ {
			fmt.Fprintf(w, "%sbhist_%s_bucket_%d %d\n", prefix, name, bmax, bh[i])
			bmax >>= 1
		}
	}

	// Application stats
	ctrs := getAllCounters()
	for name, val := range ctrs {
		fmt.Fprintf(w, "%s%s %d\n", prefix, name, val)
	}
}

// Percentiles go by 5% percentile steps from min to max. We report all of them even though it's
// likely only min, 25th, 50th, 75th, 95th, 99th, and max will be used. It's assumed the metric
// poller that is consuming this output will choose to only report to the metrics system what it
// considers useful information.
//
// Slice layout:
//  [0]: min (0th)
//  [1]: 5th
//  [n]: 5n
//  [19]: 95th
//  [20]: 99th
//  [21]: max (100th)
func hdatPercentiles(dat *hdat) []uint64 {
	buf := dat.buf
	kept := dat.kept

	if kept == 0 {
		return nil
	}
	if kept < uint64(len(buf)) {
		buf = buf[:kept]
	}

	sort.Sort(uint64slice(buf))

	pctls := make([]uint64, 22)

	// Take care of 0th and 100th specially
	pctls[0] = dat.min
	pctls[21] = dat.max

	// 5th - 95th
	for i := 1; i < 20; i++ {
		idx := len(buf) * i / 20
		pctls[i] = buf[idx]
	}

	// Add 99th
	idx := len(buf) * 99 / 100
	pctls[20] = buf[idx]

	return pctls
}

func pausePercentiles(pauses []uint64, ngc uint32) []uint64 {
	if ngc < uint32(len(pauses)) {
		pauses = pauses[:ngc]
	}

	sort.Sort(uint64slice(pauses))

	pctls := make([]uint64, 22)

	// Take care of 0th and 100th specially
	pctls[0] = pauses[0]
	pctls[21] = pauses[len(pauses)-1]

	// 5th - 95th
	for i := 1; i < 20; i++ {
		idx := len(pauses) * i / 20
		pctls[i] = pauses[idx]
	}

	// Add 99th
	idx := len(pauses) * 99 / 100
	pctls[20] = pauses[idx]

	return pctls
}

type uint64slice []uint64

func (u uint64slice) Len() int           { return len(u) }
func (u uint64slice) Swap(i, j int)      { u[i], u[j] = u[j], u[i] }
func (u uint64slice) Less(i, j int) bool { return u[i] < u[j] }
<commit_msg>Adding gauge output to the metrics endpoint<commit_after>// Copyright 2015 Netflix, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package metrics

import (
	"fmt"
	"math"
	"net/http"
	"runtime"
	"sort"
)

var prefix = ""

func SetPrefix(p string) {
	prefix = p
}

var memstats = new(runtime.MemStats)

func init() {
	http.Handle("/metrics", http.HandlerFunc(printMetrics))
}

func printMetrics(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")

	//////////////////////////
	// Runtime memory stats
	//////////////////////////
	runtime.ReadMemStats(memstats)
	// General statistics.
	fmt.Fprintf(w, "%smem_alloc %d\n", prefix, memstats.Alloc)            // bytes allocated and not yet freed
	fmt.Fprintf(w, "%smem_alloc_total %d\n", prefix, memstats.TotalAlloc) // bytes allocated (even if freed)
	fmt.Fprintf(w, "%smem_sys %d\n", prefix, memstats.Sys)                // bytes obtained from system (sum of XxxSys below)
	fmt.Fprintf(w, "%smem_ptr_lookups %d\n", prefix, memstats.Lookups)    // number of pointer lookups
	fmt.Fprintf(w, "%smem_mallocs %d\n", prefix, memstats.Mallocs)        // number of mallocs
	fmt.Fprintf(w, "%smem_frees %d\n", prefix, memstats.Frees)            // number of frees

	// Main allocation heap statistics.
	fmt.Fprintf(w, "%smem_heap_alloc %d\n", prefix, memstats.HeapAlloc)       // bytes allocated and not yet freed (same as Alloc above)
	fmt.Fprintf(w, "%smem_heap_sys %d\n", prefix, memstats.HeapSys)           // bytes obtained from system
	fmt.Fprintf(w, "%smem_heap_idle %d\n", prefix, memstats.HeapIdle)         // bytes in idle spans
	fmt.Fprintf(w, "%smem_heap_in_use %d\n", prefix, memstats.HeapInuse)      // bytes in non-idle span
	fmt.Fprintf(w, "%smem_heap_released %d\n", prefix, memstats.HeapReleased) // bytes released to the OS
	fmt.Fprintf(w, "%smem_heap_objects %d\n", prefix, memstats.HeapObjects)   // total number of allocated objects

	fmt.Fprintf(w, "%smem_stack_in_use %d\n", prefix, memstats.StackInuse) // bytes used by stack allocator
	fmt.Fprintf(w, "%smem_stack_sys %d\n", prefix, memstats.StackSys)
	fmt.Fprintf(w, "%smem_mspan_in_use %d\n", prefix, memstats.MSpanInuse) // mspan structures
	fmt.Fprintf(w, "%smem_mspan_sys %d\n", prefix, memstats.MSpanSys)
	fmt.Fprintf(w, "%smem_mcache_in_use %d\n", prefix, memstats.MCacheInuse) // mcache structures
	fmt.Fprintf(w, "%smem_mcache_sys %d\n", prefix, memstats.MCacheSys)
	fmt.Fprintf(w, "%smem_buck_hash_sys %d\n", prefix, memstats.BuckHashSys) // profiling bucket hash table
	fmt.Fprintf(w, "%smem_gc_sys %d\n", prefix, memstats.GCSys)              // GC metadata
	fmt.Fprintf(w, "%smem_other_sys %d\n", prefix, memstats.OtherSys)        // other system allocations

	fmt.Fprintf(w, "%sgc_next_gc_heap_alloc %d\n", prefix, memstats.NextGC) // next collection will happen when HeapAlloc ≥ this amount
	fmt.Fprintf(w, "%sgc_last_gc_time %d\n", prefix, memstats.LastGC)       // end time of last collection (nanoseconds since 1970)
	fmt.Fprintf(w, "%sgc_pause_total %d\n", prefix, memstats.PauseTotalNs)
	fmt.Fprintf(w, "%sgc_num_gc %d\n", prefix, memstats.NumGC)
	fmt.Fprintf(w, "%sgc_gc_cpu_frac %f\n", prefix, memstats.GCCPUFraction)

	// circular buffer of recent GC pause durations, most recent at [(NumGC+255)%256]
	pctls := pausePercentiles(memstats.PauseNs[:], memstats.NumGC)
	for i := 0; i < 20; i++ {
		p := pctls[i]
		fmt.Fprintf(w, "%sgc_pause_pctl_%d %d\n", prefix, i*5, p)
	}
	fmt.Fprintf(w, "%sgc_pause_pctl_%d %d\n", prefix, 99, pctls[20])
	fmt.Fprintf(w, "%sgc_pause_pctl_%d %d\n", prefix, 100, pctls[21])

	// Per-size allocation statistics.
	for _, b := range memstats.BySize {
		fmt.Fprintf(w, "%salloc_size_%d_mallocs %d\n", prefix, b.Size, b.Mallocs)
		fmt.Fprintf(w, "%salloc_size_%d_frees %d\n", prefix, b.Size, b.Frees)
	}

	//////////////////////////
	// Histograms
	//////////////////////////
	hists := getAllHistograms()
	for name, dat := range hists {
		fmt.Fprintf(w, "%shist_%s_count %d\n", prefix, name, dat.count)
		fmt.Fprintf(w, "%shist_%s_kept %d\n", prefix, name, dat.kept)

		if dat.total > 0 && dat.count > 0 {
			avg := float64(dat.total) / float64(dat.count)
			fmt.Fprintf(w, "%shist_%s_avg %f\n", prefix, name, avg)
		}

		pctls := hdatPercentiles(dat)
		if len(pctls) == 0 {
			continue
		}
		for i := 0; i < 20; i++ {
			p := pctls[i]
			fmt.Fprintf(w, "%shist_%s_pctl_%d %d\n", prefix, name, i*5, p)
		}
		fmt.Fprintf(w, "%shist_%s_pctl_%d %d\n", prefix, name, 99, pctls[20])
		fmt.Fprintf(w, "%shist_%s_pctl_%d %d\n", prefix, name, 100, pctls[21])
	}

	//////////////////////////
	// Bucketized histograms
	//////////////////////////
	// Buckets are based on the number of leading zeros in the number
	// so each is less than or equal to that number of ones. Therefore
	// bucket 0 means the number was greater than 0x7FFF_FFFF_FFFF_FFFF
	// and less than or equal to 0xFFFF_FFFF_FFFF_FFFF, which would be a huge
	// duration but it's a good example. As well, bucket 63 only hold values
	// 0x0 and 0x1. Bucket 62 hold 0x10 and 0x11. 61: 0x100, 0x101, 0x110, 0x111
	bhists := getAllBucketHistograms()
	for name, bh := range bhists {
		var bmax uint64 = math.MaxUint64 // 0xFFFF_FFFF_FFFF_FFFF
		for i := 0; i < 64; i++ {
			fmt.Fprintf(w, "%sbhist_%s_bucket_%d %d\n", prefix, name, bmax, bh[i])
			bmax >>= 1
		}
	}

	//////////////////////////
	// Counters
	//////////////////////////
	ctrs := getAllCounters()
	for name, val := range ctrs {
		fmt.Fprintf(w, "%s%s %d\n", prefix, name, val)
	}

	//////////////////////////
	// Gauges
	//////////////////////////
	intg, floatg := getAllGauges()
	for name, val := range intg {
		fmt.Fprintf(w, "%s%s %d\n", prefix, name, val)
	}
	for name, val := range floatg {
		fmt.Fprintf(w, "%s%s %f\n", prefix, name, val)
	}

	//////////////////////////
	// Gauge Callbacks
	//////////////////////////
	intg, floatg = getAllCallbackGauges()
	for name, val := range intg {
		fmt.Fprintf(w, "%s%s %d\n", prefix, name, val)
	}
	for name, val := range floatg {
		fmt.Fprintf(w, "%s%s %f\n", prefix, name, val)
	}
}

// Percentiles go by 5% percentile steps from min to max. We report all of them even though it's
// likely only min, 25th, 50th, 75th, 95th, 99th, and max will be used. It's assumed the metric
// poller that is consuming this output will choose to only report to the metrics system what it
// considers useful information.
//
// Slice layout:
//  [0]: min (0th)
//  [1]: 5th
//  [n]: 5n
//  [19]: 95th
//  [20]: 99th
//  [21]: max (100th)
func hdatPercentiles(dat *hdat) []uint64 {
	buf := dat.buf
	kept := dat.kept

	if kept == 0 {
		return nil
	}
	if kept < uint64(len(buf)) {
		buf = buf[:kept]
	}

	sort.Sort(uint64slice(buf))

	pctls := make([]uint64, 22)

	// Take care of 0th and 100th specially
	pctls[0] = dat.min
	pctls[21] = dat.max

	// 5th - 95th
	for i := 1; i < 20; i++ {
		idx := len(buf) * i / 20
		pctls[i] = buf[idx]
	}

	// Add 99th
	idx := len(buf) * 99 / 100
	pctls[20] = buf[idx]

	return pctls
}

func pausePercentiles(pauses []uint64, ngc uint32) []uint64 {
	if ngc < uint32(len(pauses)) {
		pauses = pauses[:ngc]
	}

	sort.Sort(uint64slice(pauses))

	pctls := make([]uint64, 22)

	// Take care of 0th and 100th specially
	pctls[0] = pauses[0]
	pctls[21] = pauses[len(pauses)-1]

	// 5th - 95th
	for i := 1; i < 20; i++ {
		idx := len(pauses) * i / 20
		pctls[i] = pauses[idx]
	}

	// Add 99th
	idx := len(pauses) * 99 / 100
	pctls[20] = pauses[idx]

	return pctls
}

type uint64slice []uint64

func (u uint64slice) Len() int           { return len(u) }
func (u uint64slice) Swap(i, j int)      { u[i], u[j] = u[j], u[i] }
func (u uint64slice) Less(i, j int) bool { return u[i] < u[j] }
<|endoftext|>


<commit_before>package domain_migration

import (
	"encoding/json"
	"errors"
	"fmt"
	"strings"
	"time"

	controller "github.com/flynn/flynn/controller/client"
	ct "github.com/flynn/flynn/controller/types"
	worker "github.com/flynn/flynn/controller/worker/types"
	"github.com/flynn/flynn/pkg/postgres"
	"github.com/flynn/flynn/pkg/tlscert"
	router "github.com/flynn/flynn/router/types"
	"github.com/flynn/que-go"
	"github.com/inconshreveable/log15"
)

// limits the number of concurrent requests to the router
const maxActiveRouteUpdates = 5

type context struct {
	db     *postgres.DB
	client controller.Client
	logger log15.Logger
}

type migration struct {
	db                 *postgres.DB
	client             controller.Client
	logger             log15.Logger
	dm                 *ct.DomainMigration
	activeRouteUpdates chan struct{}
	stop               chan struct{}
}

func (m *migration) MigrateApp(appName string, newEnv map[string]string) error {
	log := m.logger.New("app.name", appName)
	if err := m.waitForDeployment(appName); err != nil {
		return err
	}
	release, err := m.client.GetAppRelease(appName)
	if err != nil {
		log.Error("error fetching release", "error", err)
		return err
	}
	newRelease := dupRelease(release)
	for k, v := range newEnv {
		if release.Env[k] == v {
			log.Info("already migrated")
			return nil
		}
		newRelease.Env[k] = v
	}
	if err := m.client.CreateRelease(newRelease.AppID, newRelease); err != nil {
		log.Error("error creating release", "error", err)
		return err
	}
	if err := m.client.DeployAppRelease(newRelease.AppID, newRelease.ID, m.cancelDeploy()); err != nil {
		log.Error("error deploying release", "error", err)
		select {
		case <-m.stop:
			return worker.ErrStopped
		default:
			return err
		}
	}
	return nil
}

func JobHandler(db *postgres.DB, client controller.Client, logger log15.Logger) func(*que.Job) error {
	return (&context{db, client, logger}).HandleDomainMigration
}

func (m *migration) cancelDeploy() <-chan struct{} {
	ch := make(chan struct{})
	timeout := time.After(5 * time.Minute)
	go func() {
		defer close(ch)
		select {
		case <-m.stop:
		case <-timeout:
		}
	}()
	return ch
}

func (c *context) HandleDomainMigration(job *que.Job) (err error) {
	log := c.logger.New("fn", "HandleDomainMigration")
	log.Info("handling domain migration", "job_id", job.ID, "error_count", job.ErrorCount)

	var dm *ct.DomainMigration
	if err := json.Unmarshal(job.Args, &dm); err != nil {
		log.Error("error unmarshaling job", "err", err)
		return err
	}

	log = log.New("domain_migration", dm.ID)

	m := &migration{
		db:                 c.db,
		client:             c.client,
		logger:             log,
		dm:                 dm,
		activeRouteUpdates: make(chan struct{}, maxActiveRouteUpdates),
		stop:               job.Stop,
	}

	if err := m.db.QueryRow("SELECT old_domain, domain, old_tls_cert, tls_cert, created_at, finished_at FROM domain_migrations WHERE migration_id = $1", dm.ID).Scan(&dm.OldDomain, &dm.Domain, &dm.OldTLSCert, &dm.TLSCert, &dm.CreatedAt, &dm.FinishedAt); err != nil {
		log.Error("error fetching postgres record", "err", err)
		m.createEvent(err)
		return err
	}

	if dm.FinishedAt != nil {
		// Already done
		return nil
	}

	return m.Run()
}

func (m *migration) Run() error {
	log := m.logger
	dm := m.dm

	// Generate TLS Cert if not already present
	var cert *tlscert.Cert
	if dm.TLSCert == nil {
		var err error
		cert, err = m.generateTLSCert()
		if err != nil {
			log.Error("error generating TLS cert", "err", err)
			m.createEvent(err)
			return err
		}
		dm.TLSCert = cert
	}

	type AppMigration struct {
		AppName string
		NewEnv  map[string]string
	}

	appMigrations := []*AppMigration{
		{
			AppName: "controller",
			NewEnv: map[string]string{
				"DEFAULT_ROUTE_DOMAIN": m.dm.Domain,
				"CA_CERT":              m.dm.TLSCert.CACert,
			},
		},
		{
			AppName: "router",
			NewEnv: map[string]string{
				"TLSCERT": m.dm.TLSCert.Cert,
				"TLSKEY":  m.dm.TLSCert.PrivateKey,
			},
		},
		{
			AppName: "router",
			NewEnv: map[string]string{
				"TLSCERT": m.dm.TLSCert.Cert,
				"TLSKEY":  m.dm.TLSCert.PrivateKey,
			},
		},
		{
			AppName: "dashboard",
			NewEnv: map[string]string{
				"CA_CERT":              m.dm.TLSCert.CACert,
				"DEFAULT_ROUTE_DOMAIN": m.dm.Domain,
				"CONTROLLER_DOMAIN":    fmt.Sprintf("controller.%s", m.dm.Domain),
				"URL":                  fmt.Sprintf("https://dashboard.%s", m.dm.Domain),
			},
		},
	}

	for _, am := range appMigrations {
		if err := m.MigrateApp(am.AppName, am.NewEnv); err != nil {
			log.Error(fmt.Sprintf("error deploying %s", am.AppName), "err", err)
			m.createEvent(err)
			return err
		}
	}

	if err := m.createMissingRoutes(); err != nil {
		log.Error("error creating missing routes", "err", err)
		m.createEvent(err)
		return err
	}

	if err := m.db.QueryRow("UPDATE domain_migrations SET finished_at = now() RETURNING finished_at").Scan(&dm.FinishedAt); err != nil {
		log.Error("error setting finished_at", "err", err)
		m.createEvent(err)
		return err
	}

	log.Info("domain migration complete")

	m.createEvent(nil)

	return nil
}

func (m *migration) generateTLSCert() (*tlscert.Cert, error) {
	hosts := []string{
		m.dm.Domain,
		fmt.Sprintf("*.%s", m.dm.Domain),
	}
	cert, err := tlscert.Generate(hosts)
	if err != nil {
		return nil, err
	}
	if _, err := m.db.Query("UPDATE domain_migrations SET tls_cert = $1 WHERE migration_id = $2", cert, m.dm.ID); err != nil {
		return nil, err
	}
	return cert, nil
}

func dupRelease(release *ct.Release) *ct.Release {
	return &ct.Release{
		AppID:       release.AppID,
		ArtifactIDs: release.ArtifactIDs,
		Env:         release.Env,
		Meta:        release.Meta,
		Processes:   release.Processes,
	}
}

func (m *migration) waitForDeployment(app string) error {
	list, err := m.client.DeploymentList(app)
	if err != nil || len(list) == 0 {
		return err
	}
	d := list[0]
	if d.Status != "pending" && d.Status != "running" {
		return nil
	}

	events := make(chan *ct.Event)
	stream, err := m.client.StreamEvents(ct.StreamEventsOptions{
		AppID:       d.AppID,
		ObjectID:    d.ID,
		ObjectTypes: []ct.EventType{ct.EventTypeDeployment},
	}, events)
	if err != nil {
		return err
	}
	defer stream.Close()

	log := m.logger.New("app.name", app, "deployment", d.ID)
	log.Info("waiting for deployment")

	timeout := time.After(2 * time.Minute)
	for {
		select {
		case event := <-events:
			var data *ct.DeploymentEvent
			if err := json.Unmarshal(event.Data, &data); err != nil {
				return err
			}
			if data.Status == "complete" {
				log.Info("deployment complete")
				return nil
			}
			if data.Status == "failed" {
				log.Error("deployment failed", "error", data.Error)
				return errors.New(data.Error)
			}
		case <-timeout:
			err := errors.New("timed out waiting for deployment")
			log.Error(err.Error())
			return err
		case <-m.stop:
			return worker.ErrStopped
		}
	}
}

func (m *migration) createMissingRoutes() error {
	apps, err := m.client.AppList()
	if err != nil {
		return err
	}

	errChan := make(chan error, len(apps))
	createMissingRoutes := func(app *ct.App) {
		errChan <- m.appCreateMissingRoutes(app.ID)
	}
	for _, app := range apps {
		go createMissingRoutes(app)
	}
	var returnErr error
	for range apps {
		if err := <-errChan; err != nil && returnErr == nil {
			returnErr = err
		}
	}
	return returnErr
}

func (m *migration) appCreateMissingRoutes(appID string) error {
	routes, err := m.client.AppRouteList(appID)
	if err != nil {
		return err
	}
	if len(routes) == 0 {
		// There are no routes for this app
		return nil
	}
	m.activeRouteUpdates <- struct{}{}
	for _, route := range routes {
		if route.Type != "http" {
			continue
		}
		if strings.HasSuffix(route.Domain, m.dm.OldDomain) {
			if err := m.appMaybeCreateRoute(appID, route, routes); err != nil {
				return err
			}
		}
	}
	<-m.activeRouteUpdates
	return nil
}

func (m *migration) appMaybeCreateRoute(appID string, oldRoute *router.Route, routes []*router.Route) error {
	prefix := strings.TrimSuffix(oldRoute.Domain, m.dm.OldDomain)
	for _, route := range routes {
		if strings.HasPrefix(route.Domain, prefix) && strings.HasSuffix(route.Domain, m.dm.Domain) {
			// Route already exists
			return nil
		}
	}
	route := &router.Route{
		Type:          "http",
		Domain:        strings.Join([]string{prefix, m.dm.Domain}, ""),
		Sticky:        oldRoute.Sticky,
		Service:       oldRoute.Service,
		DrainBackends: oldRoute.DrainBackends,
	}
	if oldRoute.Certificate != nil && oldRoute.Certificate.Cert == strings.TrimSpace(m.dm.OldTLSCert.Cert) {
		route.Certificate = &router.Certificate{
			Cert: m.dm.TLSCert.Cert,
			Key:  m.dm.TLSCert.PrivateKey,
		}
	} else {
		route.Certificate = oldRoute.Certificate
	}
	err := m.client.CreateRoute(appID, route)
	if err != nil && err.Error() == "conflict: Duplicate route" {
		return nil
	}
	return err
}

func (m *migration) createEvent(err error) error {
	if err == worker.ErrStopped {
		return nil
	}
	e := ct.DomainMigrationEvent{DomainMigration: m.dm}
	if err != nil {
		e.Error = err.Error()
	}
	query := "INSERT INTO events (object_id, object_type, data) VALUES ($1, $2, $3)"
	return m.db.Exec(query, m.dm.ID, string(ct.EventTypeDomainMigration), e)
}
<commit_msg>controller: Add controller-grpc to domain migration worker<commit_after>package domain_migration

import (
	"encoding/json"
	"errors"
	"fmt"
	"strings"
	"time"

	controller "github.com/flynn/flynn/controller/client"
	ct "github.com/flynn/flynn/controller/types"
	worker "github.com/flynn/flynn/controller/worker/types"
	"github.com/flynn/flynn/pkg/postgres"
	"github.com/flynn/flynn/pkg/tlscert"
	router "github.com/flynn/flynn/router/types"
	"github.com/flynn/que-go"
	"github.com/inconshreveable/log15"
)

// limits the number of concurrent requests to the router
const maxActiveRouteUpdates = 5

type context struct {
	db     *postgres.DB
	client controller.Client
	logger log15.Logger
}

type migration struct {
	db                 *postgres.DB
	client             controller.Client
	logger             log15.Logger
	dm                 *ct.DomainMigration
	activeRouteUpdates chan struct{}
	stop               chan struct{}
}

func (m *migration) MigrateApp(appName string, newEnv map[string]string) error {
	log := m.logger.New("app.name", appName)
	if err := m.waitForDeployment(appName); err != nil {
		return err
	}
	release, err := m.client.GetAppRelease(appName)
	if err != nil {
		log.Error("error fetching release", "error", err)
		return err
	}
	newRelease := dupRelease(release)
	for k, v := range newEnv {
		if release.Env[k] == v {
			log.Info("already migrated")
			return nil
		}
		newRelease.Env[k] = v
	}
	if err := m.client.CreateRelease(newRelease.AppID, newRelease); err != nil {
		log.Error("error creating release", "error", err)
		return err
	}
	if err := m.client.DeployAppRelease(newRelease.AppID, newRelease.ID, m.cancelDeploy()); err != nil {
		log.Error("error deploying release", "error", err)
		select {
		case <-m.stop:
			return worker.ErrStopped
		default:
			return err
		}
	}
	return nil
}

func JobHandler(db *postgres.DB, client controller.Client, logger log15.Logger) func(*que.Job) error {
	return (&context{db, client, logger}).HandleDomainMigration
}

func (m *migration) cancelDeploy() <-chan struct{} {
	ch := make(chan struct{})
	timeout := time.After(5 * time.Minute)
	go func() {
		defer close(ch)
		select {
		case <-m.stop:
		case <-timeout:
		}
	}()
	return ch
}

func (c *context) HandleDomainMigration(job *que.Job) (err error) {
	log := c.logger.New("fn", "HandleDomainMigration")
	log.Info("handling domain migration", "job_id", job.ID, "error_count", job.ErrorCount)

	var dm *ct.DomainMigration
	if err := json.Unmarshal(job.Args, &dm); err != nil {
		log.Error("error unmarshaling job", "err", err)
		return err
	}

	log = log.New("domain_migration", dm.ID)

	m := &migration{
		db:                 c.db,
		client:             c.client,
		logger:             log,
		dm:                 dm,
		activeRouteUpdates: make(chan struct{}, maxActiveRouteUpdates),
		stop:               job.Stop,
	}

	if err := m.db.QueryRow("SELECT old_domain, domain, old_tls_cert, tls_cert, created_at, finished_at FROM domain_migrations WHERE migration_id = $1", dm.ID).Scan(&dm.OldDomain, &dm.Domain, &dm.OldTLSCert, &dm.TLSCert, &dm.CreatedAt, &dm.FinishedAt); err != nil {
		log.Error("error fetching postgres record", "err", err)
		m.createEvent(err)
		return err
	}

	if dm.FinishedAt != nil {
		// Already done
		return nil
	}

	return m.Run()
}

func (m *migration) Run() error {
	log := m.logger
	dm := m.dm

	// Generate TLS Cert if not already present
	var cert *tlscert.Cert
	if dm.TLSCert == nil {
		var err error
		cert, err = m.generateTLSCert()
		if err != nil {
			log.Error("error generating TLS cert", "err", err)
			m.createEvent(err)
			return err
		}
		dm.TLSCert = cert
	}

	type AppMigration struct {
		AppName string
		NewEnv  map[string]string
	}

	appMigrations := []*AppMigration{
		{
			AppName: "controller",
			NewEnv: map[string]string{
				"DEFAULT_ROUTE_DOMAIN": m.dm.Domain,
				"CA_CERT":              m.dm.TLSCert.CACert,
			},
		},
		{
			AppName: "controller-grpc",
			NewEnv: map[string]string{
				"DEFAULT_ROUTE_DOMAIN": m.dm.Domain,
			},
		},
		{
			AppName: "router",
			NewEnv: map[string]string{
				"TLSCERT": m.dm.TLSCert.Cert,
				"TLSKEY":  m.dm.TLSCert.PrivateKey,
			},
		},
		{
			AppName: "dashboard",
			NewEnv: map[string]string{
				"CA_CERT":              m.dm.TLSCert.CACert,
				"DEFAULT_ROUTE_DOMAIN": m.dm.Domain,
				"CONTROLLER_DOMAIN":    fmt.Sprintf("controller.%s", m.dm.Domain),
				"URL":                  fmt.Sprintf("https://dashboard.%s", m.dm.Domain),
			},
		},
	}

	for _, am := range appMigrations {
		if err := m.MigrateApp(am.AppName, am.NewEnv); err != nil {
			log.Error(fmt.Sprintf("error deploying %s", am.AppName), "err", err)
			m.createEvent(err)
			return err
		}
	}

	if err := m.createMissingRoutes(); err != nil {
		log.Error("error creating missing routes", "err", err)
		m.createEvent(err)
		return err
	}

	if err := m.db.QueryRow("UPDATE domain_migrations SET finished_at = now() RETURNING finished_at").Scan(&dm.FinishedAt); err != nil {
		log.Error("error setting finished_at", "err", err)
		m.createEvent(err)
		return err
	}

	log.Info("domain migration complete")

	m.createEvent(nil)

	return nil
}

func (m *migration) generateTLSCert() (*tlscert.Cert, error) {
	hosts := []string{
		m.dm.Domain,
		fmt.Sprintf("*.%s", m.dm.Domain),
	}
	cert, err := tlscert.Generate(hosts)
	if err != nil {
		return nil, err
	}
	if _, err := m.db.Query("UPDATE domain_migrations SET tls_cert = $1 WHERE migration_id = $2", cert, m.dm.ID); err != nil {
		return nil, err
	}
	return cert, nil
}

func dupRelease(release *ct.Release) *ct.Release {
	return &ct.Release{
		AppID:       release.AppID,
		ArtifactIDs: release.ArtifactIDs,
		Env:         release.Env,
		Meta:        release.Meta,
		Processes:   release.Processes,
	}
}

func (m *migration) waitForDeployment(app string) error {
	list, err := m.client.DeploymentList(app)
	if err != nil || len(list) == 0 {
		return err
	}
	d := list[0]
	if d.Status != "pending" && d.Status != "running" {
		return nil
	}

	events := make(chan *ct.Event)
	stream, err := m.client.StreamEvents(ct.StreamEventsOptions{
		AppID:       d.AppID,
		ObjectID:    d.ID,
		ObjectTypes: []ct.EventType{ct.EventTypeDeployment},
	}, events)
	if err != nil {
		return err
	}
	defer stream.Close()

	log := m.logger.New("app.name", app, "deployment", d.ID)
	log.Info("waiting for deployment")

	timeout := time.After(2 * time.Minute)
	for {
		select {
		case event := <-events:
			var data *ct.DeploymentEvent
			if err := json.Unmarshal(event.Data, &data); err != nil {
				return err
			}
			if data.Status == "complete" {
				log.Info("deployment complete")
				return nil
			}
			if data.Status == "failed" {
				log.Error("deployment failed", "error", data.Error)
				return errors.New(data.Error)
			}
		case <-timeout:
			err := errors.New("timed out waiting for deployment")
			log.Error(err.Error())
			return err
		case <-m.stop:
			return worker.ErrStopped
		}
	}
}

func (m *migration) createMissingRoutes() error {
	apps, err := m.client.AppList()
	if err != nil {
		return err
	}

	errChan := make(chan error, len(apps))
	createMissingRoutes := func(app *ct.App) {
		errChan <- m.appCreateMissingRoutes(app.ID)
	}
	for _, app := range apps {
		go createMissingRoutes(app)
	}
	var returnErr error
	for range apps {
		if err := <-errChan; err != nil && returnErr == nil {
			returnErr = err
		}
	}
	return returnErr
}

func (m *migration) appCreateMissingRoutes(appID string) error {
	routes, err := m.client.AppRouteList(appID)
	if err != nil {
		return err
	}
	if len(routes) == 0 {
		// There are no routes for this app
		return nil
	}
	m.activeRouteUpdates <- struct{}{}
	for _, route := range routes {
		if route.Type != "http" {
			continue
		}
		if strings.HasSuffix(route.Domain, m.dm.OldDomain) {
			if err := m.appMaybeCreateRoute(appID, route, routes); err != nil {
				return err
			}
		}
	}
	<-m.activeRouteUpdates
	return nil
}

func (m *migration) appMaybeCreateRoute(appID string, oldRoute *router.Route, routes []*router.Route) error {
	prefix := strings.TrimSuffix(oldRoute.Domain, m.dm.OldDomain)
	for _, route := range routes {
		if strings.HasPrefix(route.Domain, prefix) && strings.HasSuffix(route.Domain, m.dm.Domain) {
			// Route already exists
			return nil
		}
	}
	route := &router.Route{
		Type:          "http",
		Domain:        strings.Join([]string{prefix, m.dm.Domain}, ""),
		Sticky:        oldRoute.Sticky,
		Service:       oldRoute.Service,
		DrainBackends: oldRoute.DrainBackends,
	}
	if oldRoute.Certificate != nil && oldRoute.Certificate.Cert == strings.TrimSpace(m.dm.OldTLSCert.Cert) {
		route.Certificate = &router.Certificate{
			Cert: m.dm.TLSCert.Cert,
			Key:  m.dm.TLSCert.PrivateKey,
		}
	} else {
		route.Certificate = oldRoute.Certificate
	}
	err := m.client.CreateRoute(appID, route)
	if err != nil && err.Error() == "conflict: Duplicate route" {
		return nil
	}
	return err
}

func (m *migration) createEvent(err error) error {
	if err == worker.ErrStopped {
		return nil
	}
	e := ct.DomainMigrationEvent{DomainMigration: m.dm}
	if err != nil {
		e.Error = err.Error()
	}
	query := "INSERT INTO events (object_id, object_type, data) VALUES ($1, $2, $3)"
	return m.db.Exec(query, m.dm.ID, string(ct.EventTypeDomainMigration), e)
}
<|endoftext|>


<commit_before>package unionfs

import (
	"log"
	"sync"
	"time"
)

var _ = log.Println

type cacheEntry struct {
	data interface{}

	// expiryNs is the absolute timestamp of the expiry.
	expiryNs int64
}

// TimedIntCache caches the result of fetch() for some time.
//
// Oh, how I wish we had generics.
type TimedCache struct {
	fetch func(name string) interface{}

	// ttlNs is a duration of the cache.
	ttlNs int64

	cacheMapMutex sync.RWMutex
	cacheMap      map[string]*cacheEntry

	PurgeTimer *time.Timer
}

const layerCacheTimeoutNs = 1e9

func NewTimedCache(fetcher func(name string) interface{}, ttlNs int64) *TimedCache {
	l := new(TimedCache)
	l.ttlNs = ttlNs
	l.fetch = fetcher
	l.cacheMap = make(map[string]*cacheEntry)
	return l
}

func (me *TimedCache) Get(name string) interface{} {
	me.cacheMapMutex.RLock()
	info, ok := me.cacheMap[name]
	me.cacheMapMutex.RUnlock()

	now := time.Nanoseconds()
	if ok && info.expiryNs > now {
		return info.data
	}
	return me.getDataNoCache(name)
}

func (me *TimedCache) Set(name string, val interface{}) {
	me.cacheMapMutex.Lock()
	defer me.cacheMapMutex.Unlock()

	me.cacheMap[name] = &cacheEntry{
		data:     val,
		expiryNs: time.Nanoseconds() + me.ttlNs,
	}
}

func (me *TimedCache) getDataNoCache(name string) interface{} {
	data := me.fetch(name)
	me.Set(name, data)
	return data
}

// Drop all expired entries.
func (me *TimedCache) Purge() {
	keys := make([]string, 0, len(me.cacheMap))
	now := time.Nanoseconds()

	me.cacheMapMutex.Lock()
	defer me.cacheMapMutex.Unlock()
	for k, v := range me.cacheMap {
		if v.expiryNs < now {
			keys = append(keys, k)
		}
	}
	for _, k := range keys {
		me.cacheMap[k] = nil, false
	}
}

func (me *TimedCache) RecurringPurge() {
	me.Purge()
	me.PurgeTimer = time.AfterFunc(5*me.ttlNs,
		func() { me.RecurringPurge() })
}
<commit_msg>Add a TimedCache.DropEntry method.<commit_after>package unionfs

import (
	"log"
	"sync"
	"time"
)

var _ = log.Println

type cacheEntry struct {
	data interface{}

	// expiryNs is the absolute timestamp of the expiry.
	expiryNs int64
}

// TimedIntCache caches the result of fetch() for some time.
//
// Oh, how I wish we had generics.
type TimedCache struct {
	fetch func(name string) interface{}

	// ttlNs is a duration of the cache.
	ttlNs int64

	cacheMapMutex sync.RWMutex
	cacheMap      map[string]*cacheEntry

	PurgeTimer *time.Timer
}

const layerCacheTimeoutNs = 1e9

func NewTimedCache(fetcher func(name string) interface{}, ttlNs int64) *TimedCache {
	l := new(TimedCache)
	l.ttlNs = ttlNs
	l.fetch = fetcher
	l.cacheMap = make(map[string]*cacheEntry)
	return l
}

func (me *TimedCache) Get(name string) interface{} {
	me.cacheMapMutex.RLock()
	info, ok := me.cacheMap[name]
	me.cacheMapMutex.RUnlock()

	now := time.Nanoseconds()
	if ok && info.expiryNs > now {
		return info.data
	}
	return me.getDataNoCache(name)
}

func (me *TimedCache) Set(name string, val interface{}) {
	me.cacheMapMutex.Lock()
	defer me.cacheMapMutex.Unlock()

	me.cacheMap[name] = &cacheEntry{
		data:     val,
		expiryNs: time.Nanoseconds() + me.ttlNs,
	}
}

func (me *TimedCache) DropEntry(name string) {
	me.cacheMapMutex.Lock()
	defer me.cacheMapMutex.Unlock()

	me.cacheMap[name] = nil, false
}

func (me *TimedCache) getDataNoCache(name string) interface{} {
	data := me.fetch(name)
	me.Set(name, data)
	return data
}

// Drop all expired entries.
func (me *TimedCache) Purge() {
	keys := make([]string, 0, len(me.cacheMap))
	now := time.Nanoseconds()

	me.cacheMapMutex.Lock()
	defer me.cacheMapMutex.Unlock()
	for k, v := range me.cacheMap {
		if v.expiryNs < now {
			keys = append(keys, k)
		}
	}
	for _, k := range keys {
		me.cacheMap[k] = nil, false
	}
}

func (me *TimedCache) RecurringPurge() {
	me.Purge()
	me.PurgeTimer = time.AfterFunc(5*me.ttlNs,
		func() { me.RecurringPurge() })
}
<|endoftext|>


<commit_before>package qmp

import (
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/pkg/errors"

	"github.com/lxc/lxd/lxd/revert"
	"github.com/lxc/lxd/shared"
)

// Status returns the current VM status.
func (m *Monitor) Status() (string, error) {
	// Prepare the response.
	var resp struct {
		Return struct {
			Status string `json:"status"`
		} `json:"return"`
	}

	// Query the status.
	err := m.run("query-status", nil, &resp)
	if err != nil {
		return "", err
	}

	return resp.Return.Status, nil
}

// Console fetches the File for a particular console.
func (m *Monitor) Console(target string) (*os.File, error) {
	// Prepare the response.
	var resp struct {
		Return []struct {
			Label    string `json:"label"`
			Filename string `json:"filename"`
		} `json:"return"`
	}

	// Query the consoles.
	err := m.run("query-chardev", nil, &resp)
	if err != nil {
		return nil, err
	}

	// Look for the requested console.
	for _, v := range resp.Return {
		if v.Label == target {
			ptyPath := strings.TrimPrefix(v.Filename, "pty:")

			if !shared.PathExists(ptyPath) {
				continue
			}

			// Open the PTS device
			console, err := os.OpenFile(ptyPath, os.O_RDWR, 0600)
			if err != nil {
				return nil, err
			}

			return console, nil
		}
	}

	return nil, ErrMonitorBadConsole
}

// SendFile adds a new file descriptor to the QMP fd table associated to name.
func (m *Monitor) SendFile(name string, file *os.File) error {
	// Check if disconnected
	if m.disconnected {
		return ErrMonitorDisconnect
	}

	// Query the status.
	_, err := m.qmp.RunWithFile([]byte(fmt.Sprintf("{'execute': 'getfd', 'arguments': {'fdname': '%s'}}", name)), file)
	if err != nil {
		// Confirm the daemon didn't die.
		errPing := m.ping()
		if errPing != nil {
			return errPing
		}

		return err
	}

	return nil
}

// Migrate starts a migration stream.
func (m *Monitor) Migrate(uri string) error {
	// Query the status.
	args := map[string]string{"uri": uri}
	err := m.run("migrate", args, nil)
	if err != nil {
		return err
	}

	// Wait until it completes or fails.
	for {
		time.Sleep(1 * time.Second)

		// Prepare the response.
		var resp struct {
			Return struct {
				Status string `json:"status"`
			} `json:"return"`
		}

		err := m.run("query-migrate", nil, &resp)
		if err != nil {
			return err
		}

		if resp.Return.Status == "failed" {
			return fmt.Errorf("Migration call failed")
		}

		if resp.Return.Status == "completed" {
			break
		}
	}

	return nil
}

// MigrateIncoming starts the receiver of a migration stream.
func (m *Monitor) MigrateIncoming(uri string) error {
	// Query the status.
	args := map[string]string{"uri": uri}
	err := m.run("migrate-incoming", args, nil)
	if err != nil {
		return err
	}

	// Wait until it completes or fails.
	for {
		time.Sleep(1 * time.Second)

		// Preapre the response.
		var resp struct {
			Return struct {
				Status string `json:"status"`
			} `json:"return"`
		}

		err := m.run("query-migrate", nil, &resp)
		if err != nil {
			return err
		}

		if resp.Return.Status == "failed" {
			return fmt.Errorf("Migration call failed")
		}

		if resp.Return.Status == "completed" {
			break
		}
	}

	return nil
}

// Powerdown tells the VM to gracefully shutdown.
func (m *Monitor) Powerdown() error {
	return m.run("system_powerdown", nil, nil)
}

// Start tells QEMU to start the emulation.
func (m *Monitor) Start() error {
	return m.run("cont", nil, nil)
}

// Pause tells QEMU to temporarily stop the emulation.
func (m *Monitor) Pause() error {
	return m.run("stop", nil, nil)
}

// Quit tells QEMU to exit immediately.
func (m *Monitor) Quit() error {
	return m.run("quit", nil, nil)
}

// GetCPUs fetches the vCPU information for pinning.
func (m *Monitor) GetCPUs() ([]int, error) {
	// Prepare the response.
	var resp struct {
		Return []struct {
			CPU int `json:"cpu-index"`
			PID int `json:"thread-id"`
		} `json:"return"`
	}

	// Query the consoles.
	err := m.run("query-cpus-fast", nil, &resp)
	if err != nil {
		return nil, err
	}

	// Make a slice of PIDs.
	pids := []int{}
	for _, cpu := range resp.Return {
		pids = append(pids, cpu.PID)
	}

	return pids, nil
}

// GetMemorySizeBytes returns the current size of the base memory in bytes.
func (m *Monitor) GetMemorySizeBytes() (int64, error) {
	// Prepare the response.
	var resp struct {
		Return struct {
			BaseMemory int64 `json:"base-memory"`
		} `json:"return"`
	}

	err := m.run("query-memory-size-summary", nil, &resp)
	if err != nil {
		return -1, err
	}

	return resp.Return.BaseMemory, nil
}

// GetMemoryBalloonSizeBytes returns effective size of the memory in bytes (considering the current balloon size).
func (m *Monitor) GetMemoryBalloonSizeBytes() (int64, error) {
	// Prepare the response.
	var resp struct {
		Return struct {
			Actual int64 `json:"actual"`
		} `json:"return"`
	}

	err := m.run("query-balloon", nil, &resp)
	if err != nil {
		return -1, err
	}

	return resp.Return.Actual, nil
}

// SetMemoryBalloonSizeBytes sets the size of the memory in bytes (which will resize the balloon as needed).
func (m *Monitor) SetMemoryBalloonSizeBytes(sizeBytes int64) error {
	args := map[string]int64{"value": sizeBytes}
	return m.run("balloon", args, nil)
}

// AddNIC adds a NIC device.
func (m *Monitor) AddNIC(netDev map[string]interface{}, device map[string]string) error {
	revert := revert.New()
	defer revert.Fail()

	if netDev != nil {
		err := m.run("netdev_add", netDev, nil)
		if err != nil {
			return errors.Wrapf(err, "Failed adding NIC netdev")
		}

		revert.Add(func() {
			netDevDel := map[string]interface{}{
				"id": netDev["id"],
			}

			err = m.run("netdev_del", netDevDel, nil)
			if err != nil {
				return
			}
		})
	}

	if device != nil {
		err := m.run("device_add", device, nil)
		if err != nil {
			return errors.Wrapf(err, "Failed adding NIC device")
		}
	}

	revert.Success()
	return nil
}

// RemoveNIC removes a NIC device.
func (m *Monitor) RemoveNIC(netDevID string, deviceID string) error {
	if deviceID != "" {
		deviceID := map[string]string{
			"id": deviceID,
		}

		err := m.run("device_del", deviceID, nil)
		if err != nil {
			// If the device has already been removed then all good.
			if err != nil && !strings.Contains(err.Error(), "not found") {
				return errors.Wrapf(err, "Failed removing NIC device")
			}
		}
	}

	if netDevID != "" {
		netDevID := map[string]string{
			"id": netDevID,
		}

		err := m.run("netdev_del", netDevID, nil)

		// Not all NICs need a netdev, so if its missing, its not a problem.
		if err != nil && !strings.Contains(err.Error(), "not found") {
			return errors.Wrapf(err, "Failed removing NIC netdev")
		}
	}

	return nil
}

// Reset VM.
func (m *Monitor) Reset() error {
	err := m.run("system_reset", nil, nil)
	if err != nil {
		return errors.Wrapf(err, "Failed resetting")
	}

	return nil
}

// PCIClassInfo info about a device's class.
type PCIClassInfo struct {
	Class       int    `json:"class"`
	Description string `json:"desc"`
}

// PCIDevice represents a PCI device.
type PCIDevice struct {
	DevID    string       `json:"qdev_id"`
	Bus      int          `json:"bus"`
	Slot     int          `json:"slot"`
	Function int          `json:"function"`
	Devices  []PCIDevice  `json:"devices"`
	Class    PCIClassInfo `json:"class_info"`
	Bridge   PCIBridge    `json:"pci_bridge"`
}

// PCIBridge represents a PCI bridge.
type PCIBridge struct {
	Devices []PCIDevice `json:"devices"`
}

// QueryPCI returns info about PCI devices.
func (m *Monitor) QueryPCI() ([]PCIDevice, error) {
	// Prepare the response.
	var resp struct {
		Return []struct {
			Devices []PCIDevice `json:"devices"`
		} `json:"return"`
	}

	err := m.run("query-pci", nil, &resp)
	if err != nil {
		return nil, errors.Wrapf(err, "Failed querying PCI devices")
	}

	if len(resp.Return) > 0 {
		return resp.Return[0].Devices, nil
	}

	return nil, nil
}

// MemoryStats represents memory stats.
type MemoryStats struct {
	HTLBPGAlloc     int `json:"stat-htlb-pgalloc"`
	SwapOut         int `json:"stat-swap-out"`
	AvailableMemory int `json:"stat-available-memory"`
	HTLBPGFail      int `json:"stat-htlb-pgfail"`
	FreeMemory      int `json:"stat-free-memory"`
	MinorFaults     int `json:"stat-minor-faults"`
	MajorFaults     int `json:"stat-major-faults"`
	TotalMemory     int `json:"stat-total-memory"`
	SwapIn          int `json:"stat-swap-in"`
	DiskCaches      int `json:"stat-disk-caches"`
}

// GetMemoryStats return memory stats.
func (m *Monitor) GetMemoryStats() (*MemoryStats, error) {
	// Prepare the response
	var resp struct {
		Return struct {
			Stats MemoryStats `json:"stats"`
		} `json:"return"`
	}

	args := map[string]string{
		"path":     "/machine/peripheral/qemu_balloon",
		"property": "guest-stats",
	}

	err := m.run("qom-get", args, &resp)
	if err != nil {
		return nil, errors.Wrapf(err, "Failed querying memory stats")
	}

	return &resp.Return.Stats, nil
}

// BlockStats represents block device stats.
type BlockStats struct {
	BytesWritten    int `json:"wr_bytes"`
	WritesCompleted int `json:"wr_operations"`
	BytesRead       int `json:"rd_bytes"`
	ReadsCompleted  int `json:"rd_operations"`
}

// GetBlockStats return block device stats.
func (m *Monitor) GetBlockStats() (map[string]BlockStats, error) {
	// Prepare the response
	var resp struct {
		Return []struct {
			Stats BlockStats `json:"stats"`
			QDev  string     `json:"qdev"`
		} `json:"return"`
	}

	err := m.run("query-blockstats", nil, &resp)
	if err != nil {
		return nil, errors.Wrapf(err, "Failed querying block stats")
	}

	out := make(map[string]BlockStats)

	for _, res := range resp.Return {
		out[res.QDev] = res.Stats
	}

	return out, nil
}
<commit_msg>lxd/instance/qemu/qmp: Remove GetMemoryStats<commit_after>package qmp

import (
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/pkg/errors"

	"github.com/lxc/lxd/lxd/revert"
	"github.com/lxc/lxd/shared"
)

// Status returns the current VM status.
func (m *Monitor) Status() (string, error) {
	// Prepare the response.
	var resp struct {
		Return struct {
			Status string `json:"status"`
		} `json:"return"`
	}

	// Query the status.
	err := m.run("query-status", nil, &resp)
	if err != nil {
		return "", err
	}

	return resp.Return.Status, nil
}

// Console fetches the File for a particular console.
func (m *Monitor) Console(target string) (*os.File, error) {
	// Prepare the response.
	var resp struct {
		Return []struct {
			Label    string `json:"label"`
			Filename string `json:"filename"`
		} `json:"return"`
	}

	// Query the consoles.
	err := m.run("query-chardev", nil, &resp)
	if err != nil {
		return nil, err
	}

	// Look for the requested console.
	for _, v := range resp.Return {
		if v.Label == target {
			ptyPath := strings.TrimPrefix(v.Filename, "pty:")

			if !shared.PathExists(ptyPath) {
				continue
			}

			// Open the PTS device
			console, err := os.OpenFile(ptyPath, os.O_RDWR, 0600)
			if err != nil {
				return nil, err
			}

			return console, nil
		}
	}

	return nil, ErrMonitorBadConsole
}

// SendFile adds a new file descriptor to the QMP fd table associated to name.
func (m *Monitor) SendFile(name string, file *os.File) error {
	// Check if disconnected
	if m.disconnected {
		return ErrMonitorDisconnect
	}

	// Query the status.
	_, err := m.qmp.RunWithFile([]byte(fmt.Sprintf("{'execute': 'getfd', 'arguments': {'fdname': '%s'}}", name)), file)
	if err != nil {
		// Confirm the daemon didn't die.
		errPing := m.ping()
		if errPing != nil {
			return errPing
		}

		return err
	}

	return nil
}

// Migrate starts a migration stream.
func (m *Monitor) Migrate(uri string) error {
	// Query the status.
	args := map[string]string{"uri": uri}
	err := m.run("migrate", args, nil)
	if err != nil {
		return err
	}

	// Wait until it completes or fails.
	for {
		time.Sleep(1 * time.Second)

		// Prepare the response.
		var resp struct {
			Return struct {
				Status string `json:"status"`
			} `json:"return"`
		}

		err := m.run("query-migrate", nil, &resp)
		if err != nil {
			return err
		}

		if resp.Return.Status == "failed" {
			return fmt.Errorf("Migration call failed")
		}

		if resp.Return.Status == "completed" {
			break
		}
	}

	return nil
}

// MigrateIncoming starts the receiver of a migration stream.
func (m *Monitor) MigrateIncoming(uri string) error {
	// Query the status.
	args := map[string]string{"uri": uri}
	err := m.run("migrate-incoming", args, nil)
	if err != nil {
		return err
	}

	// Wait until it completes or fails.
	for {
		time.Sleep(1 * time.Second)

		// Preapre the response.
		var resp struct {
			Return struct {
				Status string `json:"status"`
			} `json:"return"`
		}

		err := m.run("query-migrate", nil, &resp)
		if err != nil {
			return err
		}

		if resp.Return.Status == "failed" {
			return fmt.Errorf("Migration call failed")
		}

		if resp.Return.Status == "completed" {
			break
		}
	}

	return nil
}

// Powerdown tells the VM to gracefully shutdown.
func (m *Monitor) Powerdown() error {
	return m.run("system_powerdown", nil, nil)
}

// Start tells QEMU to start the emulation.
func (m *Monitor) Start() error {
	return m.run("cont", nil, nil)
}

// Pause tells QEMU to temporarily stop the emulation.
func (m *Monitor) Pause() error {
	return m.run("stop", nil, nil)
}

// Quit tells QEMU to exit immediately.
func (m *Monitor) Quit() error {
	return m.run("quit", nil, nil)
}

// GetCPUs fetches the vCPU information for pinning.
func (m *Monitor) GetCPUs() ([]int, error) {
	// Prepare the response.
	var resp struct {
		Return []struct {
			CPU int `json:"cpu-index"`
			PID int `json:"thread-id"`
		} `json:"return"`
	}

	// Query the consoles.
	err := m.run("query-cpus-fast", nil, &resp)
	if err != nil {
		return nil, err
	}

	// Make a slice of PIDs.
	pids := []int{}
	for _, cpu := range resp.Return {
		pids = append(pids, cpu.PID)
	}

	return pids, nil
}

// GetMemorySizeBytes returns the current size of the base memory in bytes.
func (m *Monitor) GetMemorySizeBytes() (int64, error) {
	// Prepare the response.
	var resp struct {
		Return struct {
			BaseMemory int64 `json:"base-memory"`
		} `json:"return"`
	}

	err := m.run("query-memory-size-summary", nil, &resp)
	if err != nil {
		return -1, err
	}

	return resp.Return.BaseMemory, nil
}

// GetMemoryBalloonSizeBytes returns effective size of the memory in bytes (considering the current balloon size).
func (m *Monitor) GetMemoryBalloonSizeBytes() (int64, error) {
	// Prepare the response.
	var resp struct {
		Return struct {
			Actual int64 `json:"actual"`
		} `json:"return"`
	}

	err := m.run("query-balloon", nil, &resp)
	if err != nil {
		return -1, err
	}

	return resp.Return.Actual, nil
}

// SetMemoryBalloonSizeBytes sets the size of the memory in bytes (which will resize the balloon as needed).
func (m *Monitor) SetMemoryBalloonSizeBytes(sizeBytes int64) error {
	args := map[string]int64{"value": sizeBytes}
	return m.run("balloon", args, nil)
}

// AddNIC adds a NIC device.
func (m *Monitor) AddNIC(netDev map[string]interface{}, device map[string]string) error {
	revert := revert.New()
	defer revert.Fail()

	if netDev != nil {
		err := m.run("netdev_add", netDev, nil)
		if err != nil {
			return errors.Wrapf(err, "Failed adding NIC netdev")
		}

		revert.Add(func() {
			netDevDel := map[string]interface{}{
				"id": netDev["id"],
			}

			err = m.run("netdev_del", netDevDel, nil)
			if err != nil {
				return
			}
		})
	}

	if device != nil {
		err := m.run("device_add", device, nil)
		if err != nil {
			return errors.Wrapf(err, "Failed adding NIC device")
		}
	}

	revert.Success()
	return nil
}

// RemoveNIC removes a NIC device.
func (m *Monitor) RemoveNIC(netDevID string, deviceID string) error {
	if deviceID != "" {
		deviceID := map[string]string{
			"id": deviceID,
		}

		err := m.run("device_del", deviceID, nil)
		if err != nil {
			// If the device has already been removed then all good.
			if err != nil && !strings.Contains(err.Error(), "not found") {
				return errors.Wrapf(err, "Failed removing NIC device")
			}
		}
	}

	if netDevID != "" {
		netDevID := map[string]string{
			"id": netDevID,
		}

		err := m.run("netdev_del", netDevID, nil)

		// Not all NICs need a netdev, so if its missing, its not a problem.
		if err != nil && !strings.Contains(err.Error(), "not found") {
			return errors.Wrapf(err, "Failed removing NIC netdev")
		}
	}

	return nil
}

// Reset VM.
func (m *Monitor) Reset() error {
	err := m.run("system_reset", nil, nil)
	if err != nil {
		return errors.Wrapf(err, "Failed resetting")
	}

	return nil
}

// PCIClassInfo info about a device's class.
type PCIClassInfo struct {
	Class       int    `json:"class"`
	Description string `json:"desc"`
}

// PCIDevice represents a PCI device.
type PCIDevice struct {
	DevID    string       `json:"qdev_id"`
	Bus      int          `json:"bus"`
	Slot     int          `json:"slot"`
	Function int          `json:"function"`
	Devices  []PCIDevice  `json:"devices"`
	Class    PCIClassInfo `json:"class_info"`
	Bridge   PCIBridge    `json:"pci_bridge"`
}

// PCIBridge represents a PCI bridge.
type PCIBridge struct {
	Devices []PCIDevice `json:"devices"`
}

// QueryPCI returns info about PCI devices.
func (m *Monitor) QueryPCI() ([]PCIDevice, error) {
	// Prepare the response.
	var resp struct {
		Return []struct {
			Devices []PCIDevice `json:"devices"`
		} `json:"return"`
	}

	err := m.run("query-pci", nil, &resp)
	if err != nil {
		return nil, errors.Wrapf(err, "Failed querying PCI devices")
	}

	if len(resp.Return) > 0 {
		return resp.Return[0].Devices, nil
	}

	return nil, nil
}

// BlockStats represents block device stats.
type BlockStats struct {
	BytesWritten    int `json:"wr_bytes"`
	WritesCompleted int `json:"wr_operations"`
	BytesRead       int `json:"rd_bytes"`
	ReadsCompleted  int `json:"rd_operations"`
}

// GetBlockStats return block device stats.
func (m *Monitor) GetBlockStats() (map[string]BlockStats, error) {
	// Prepare the response
	var resp struct {
		Return []struct {
			Stats BlockStats `json:"stats"`
			QDev  string     `json:"qdev"`
		} `json:"return"`
	}

	err := m.run("query-blockstats", nil, &resp)
	if err != nil {
		return nil, errors.Wrapf(err, "Failed querying block stats")
	}

	out := make(map[string]BlockStats)

	for _, res := range resp.Return {
		out[res.QDev] = res.Stats
	}

	return out, nil
}
<|endoftext|>


<commit_before>// Copyright 2011 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.

// +build darwin dragonfly freebsd linux netbsd openbsd solaris

// Socket control messages

package unix

import "unsafe"

// Round the length of a raw sockaddr up to align it properly.
func cmsgAlignOf(salen int) int {
	salign := sizeofPtr
	// NOTE: It seems like 64-bit Darwin and DragonFly BSD kernels
	// still require 32-bit aligned access to network subsystem.
	if darwin64Bit || dragonfly64Bit {
		salign = 4
	}
	return (salen + salign - 1) & ^(salign - 1)
}

// CmsgLen returns the value to store in the Len field of the Cmsghdr
// structure, taking into account any necessary alignment.
func CmsgLen(datalen int) int {
	return cmsgAlignOf(SizeofCmsghdr) + datalen
}

// CmsgSpace returns the number of bytes an ancillary element with
// payload of the passed data length occupies.
func CmsgSpace(datalen int) int {
	return cmsgAlignOf(SizeofCmsghdr) + cmsgAlignOf(datalen)
}

func cmsgData(h *Cmsghdr) unsafe.Pointer {
	return unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(cmsgAlignOf(SizeofCmsghdr)))
}

// SocketControlMessage represents a socket control message.
type SocketControlMessage struct {
	Header Cmsghdr
	Data   []byte
}

// ParseSocketControlMessage parses b as an array of socket control
// messages.
func ParseSocketControlMessage(b []byte) ([]SocketControlMessage, error) {
	var msgs []SocketControlMessage
	i := 0
	for i+CmsgLen(0) <= len(b) {
		h, dbuf, err := socketControlMessageHeaderAndData(b[i:])
		if err != nil {
			return nil, err
		}
		m := SocketControlMessage{Header: *h, Data: dbuf}
		msgs = append(msgs, m)
		i += cmsgAlignOf(int(h.Len))
	}
	return msgs, nil
}

func socketControlMessageHeaderAndData(b []byte) (*Cmsghdr, []byte, error) {
	h := (*Cmsghdr)(unsafe.Pointer(&b[0]))
	if h.Len < SizeofCmsghdr || int(h.Len) > len(b) {
		return nil, nil, EINVAL
	}
	return h, b[cmsgAlignOf(SizeofCmsghdr):h.Len], nil
}

// UnixRights encodes a set of open file descriptors into a socket
// control message for sending to another process.
func UnixRights(fds ...int) []byte {
	datalen := len(fds) * 4
	b := make([]byte, CmsgSpace(datalen))
	h := (*Cmsghdr)(unsafe.Pointer(&b[0]))
	h.Level = SOL_SOCKET
	h.Type = SCM_RIGHTS
	h.SetLen(CmsgLen(datalen))
	data := cmsgData(h)
	for _, fd := range fds {
		*(*int32)(data) = int32(fd)
		data = unsafe.Pointer(uintptr(data) + 4)
	}
	return b
}

// ParseUnixRights decodes a socket control message that contains an
// integer array of open file descriptors from another process.
func ParseUnixRights(m *SocketControlMessage) ([]int, error) {
	if m.Header.Level != SOL_SOCKET {
		return nil, EINVAL
	}
	if m.Header.Type != SCM_RIGHTS {
		return nil, EINVAL
	}
	fds := make([]int, len(m.Data)>>2)
	for i, j := 0, 0; i < len(m.Data); i += 4 {
		fds[j] = int(*(*int32)(unsafe.Pointer(&m.Data[i])))
		j++
	}
	return fds, nil
}
<commit_msg>unix: fix uint64->int cast of control message header<commit_after>// Copyright 2011 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.

// +build darwin dragonfly freebsd linux netbsd openbsd solaris

// Socket control messages

package unix

import "unsafe"

// Round the length of a raw sockaddr up to align it properly.
func cmsgAlignOf(salen int) int {
	salign := sizeofPtr
	// NOTE: It seems like 64-bit Darwin and DragonFly BSD kernels
	// still require 32-bit aligned access to network subsystem.
	if darwin64Bit || dragonfly64Bit {
		salign = 4
	}
	return (salen + salign - 1) & ^(salign - 1)
}

// CmsgLen returns the value to store in the Len field of the Cmsghdr
// structure, taking into account any necessary alignment.
func CmsgLen(datalen int) int {
	return cmsgAlignOf(SizeofCmsghdr) + datalen
}

// CmsgSpace returns the number of bytes an ancillary element with
// payload of the passed data length occupies.
func CmsgSpace(datalen int) int {
	return cmsgAlignOf(SizeofCmsghdr) + cmsgAlignOf(datalen)
}

func cmsgData(h *Cmsghdr) unsafe.Pointer {
	return unsafe.Pointer(uintptr(unsafe.Pointer(h)) + uintptr(cmsgAlignOf(SizeofCmsghdr)))
}

// SocketControlMessage represents a socket control message.
type SocketControlMessage struct {
	Header Cmsghdr
	Data   []byte
}

// ParseSocketControlMessage parses b as an array of socket control
// messages.
func ParseSocketControlMessage(b []byte) ([]SocketControlMessage, error) {
	var msgs []SocketControlMessage
	i := 0
	for i+CmsgLen(0) <= len(b) {
		h, dbuf, err := socketControlMessageHeaderAndData(b[i:])
		if err != nil {
			return nil, err
		}
		m := SocketControlMessage{Header: *h, Data: dbuf}
		msgs = append(msgs, m)
		i += cmsgAlignOf(int(h.Len))
	}
	return msgs, nil
}

func socketControlMessageHeaderAndData(b []byte) (*Cmsghdr, []byte, error) {
	h := (*Cmsghdr)(unsafe.Pointer(&b[0]))
	if h.Len < SizeofCmsghdr || uint64(h.Len) > uint64(len(b)) {
		return nil, nil, EINVAL
	}
	return h, b[cmsgAlignOf(SizeofCmsghdr):h.Len], nil
}

// UnixRights encodes a set of open file descriptors into a socket
// control message for sending to another process.
func UnixRights(fds ...int) []byte {
	datalen := len(fds) * 4
	b := make([]byte, CmsgSpace(datalen))
	h := (*Cmsghdr)(unsafe.Pointer(&b[0]))
	h.Level = SOL_SOCKET
	h.Type = SCM_RIGHTS
	h.SetLen(CmsgLen(datalen))
	data := cmsgData(h)
	for _, fd := range fds {
		*(*int32)(data) = int32(fd)
		data = unsafe.Pointer(uintptr(data) + 4)
	}
	return b
}

// ParseUnixRights decodes a socket control message that contains an
// integer array of open file descriptors from another process.
func ParseUnixRights(m *SocketControlMessage) ([]int, error) {
	if m.Header.Level != SOL_SOCKET {
		return nil, EINVAL
	}
	if m.Header.Type != SCM_RIGHTS {
		return nil, EINVAL
	}
	fds := make([]int, len(m.Data)>>2)
	for i, j := 0, 0; i < len(m.Data); i += 4 {
		fds[j] = int(*(*int32)(unsafe.Pointer(&m.Data[i])))
		j++
	}
	return fds, nil
}
<|endoftext|>