<commit_before>// +build linux

package fs2

import (
	stdErrors "errors"
	"os"
	"strings"

	"github.com/opencontainers/runc/libcontainer/cgroups/fscommon"
	"github.com/opencontainers/runc/libcontainer/configs"
	"github.com/pkg/errors"
	"golang.org/x/sys/unix"
)

func setFreezer(dirPath string, state configs.FreezerState) error {
	if err := supportsFreezer(dirPath); err != nil {
		// We can ignore this request as long as the user didn't ask us to
		// freeze the container (since without the freezer cgroup, that's a
		// no-op).
		if state == configs.Undefined || state == configs.Thawed {
			err = nil
		}
		return errors.Wrap(err, "freezer not supported")
	}

	var stateStr string
	switch state {
	case configs.Undefined:
		return nil
	case configs.Frozen:
		stateStr = "1"
	case configs.Thawed:
		stateStr = "0"
	default:
		return errors.Errorf("invalid freezer state %q requested", state)
	}

	if err := fscommon.WriteFile(dirPath, "cgroup.freeze", stateStr); err != nil {
		return err
	}
	// Confirm that the cgroup did actually change states.
	if actualState, err := getFreezer(dirPath); err != nil {
		return err
	} else if actualState != state {
		return errors.Errorf(`expected "cgroup.freeze" to be in state %q but was in %q`, state, actualState)
	}
	return nil
}

func supportsFreezer(dirPath string) error {
	_, err := fscommon.ReadFile(dirPath, "cgroup.freeze")
	return err
}

func getFreezer(dirPath string) (configs.FreezerState, error) {
	state, err := fscommon.ReadFile(dirPath, "cgroup.freeze")
	if err != nil {
		// If the kernel is too old, then we just treat the freezer as being in
		// an "undefined" state.
		if os.IsNotExist(err) || stdErrors.Is(err, unix.ENODEV) {
			err = nil
		}
		return configs.Undefined, err
	}
	switch strings.TrimSpace(state) {
	case "0":
		return configs.Thawed, nil
	case "1":
		return configs.Frozen, nil
	default:
		return configs.Undefined, errors.Errorf(`unknown "cgroup.freeze" state: %q`, state)
	}
}
<commit_msg>setFreezer: explicitly return nil<commit_after>// +build linux

package fs2

import (
	stdErrors "errors"
	"os"
	"strings"

	"github.com/opencontainers/runc/libcontainer/cgroups/fscommon"
	"github.com/opencontainers/runc/libcontainer/configs"
	"github.com/pkg/errors"
	"golang.org/x/sys/unix"
)

func setFreezer(dirPath string, state configs.FreezerState) error {
	if err := supportsFreezer(dirPath); err != nil {
		// We can ignore this request as long as the user didn't ask us to
		// freeze the container (since without the freezer cgroup, that's a
		// no-op).
		if state == configs.Undefined || state == configs.Thawed {
			return nil
		}
		return errors.Wrap(err, "freezer not supported")
	}

	var stateStr string
	switch state {
	case configs.Undefined:
		return nil
	case configs.Frozen:
		stateStr = "1"
	case configs.Thawed:
		stateStr = "0"
	default:
		return errors.Errorf("invalid freezer state %q requested", state)
	}

	if err := fscommon.WriteFile(dirPath, "cgroup.freeze", stateStr); err != nil {
		return err
	}
	// Confirm that the cgroup did actually change states.
	if actualState, err := getFreezer(dirPath); err != nil {
		return err
	} else if actualState != state {
		return errors.Errorf(`expected "cgroup.freeze" to be in state %q but was in %q`, state, actualState)
	}
	return nil
}

func supportsFreezer(dirPath string) error {
	_, err := fscommon.ReadFile(dirPath, "cgroup.freeze")
	return err
}

func getFreezer(dirPath string) (configs.FreezerState, error) {
	state, err := fscommon.ReadFile(dirPath, "cgroup.freeze")
	if err != nil {
		// If the kernel is too old, then we just treat the freezer as being in
		// an "undefined" state.
		if os.IsNotExist(err) || stdErrors.Is(err, unix.ENODEV) {
			err = nil
		}
		return configs.Undefined, err
	}
	switch strings.TrimSpace(state) {
	case "0":
		return configs.Thawed, nil
	case "1":
		return configs.Frozen, nil
	default:
		return configs.Undefined, errors.Errorf(`unknown "cgroup.freeze" state: %q`, state)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"strings"

	"github.com/docker/docker/pkg/integration/checker"
	"github.com/go-check/check"
)

func (s *DockerSuite) TestRmContainerWithRemovedVolume(c *check.C) {
	testRequires(c, DaemonIsLinux)
	testRequires(c, SameHostDaemon)

	dockerCmd(c, "run", "--name", "losemyvolumes", "-v", "/tmp/testing:/test", "busybox", "true")

	err := os.Remove("/tmp/testing")
	c.Assert(err, check.IsNil)

	dockerCmd(c, "rm", "-v", "losemyvolumes")
}

func (s *DockerSuite) TestRmContainerWithVolume(c *check.C) {
	testRequires(c, DaemonIsLinux)
	dockerCmd(c, "run", "--name", "foo", "-v", "/srv", "busybox", "true")

	dockerCmd(c, "rm", "-v", "foo")
}

func (s *DockerSuite) TestRmRunningContainer(c *check.C) {
	testRequires(c, DaemonIsLinux)
	createRunningContainer(c, "foo")

	_, _, err := dockerCmdWithError("rm", "foo")
	c.Assert(err, checker.NotNil, check.Commentf("Expected error, can't rm a running container"))
}

func (s *DockerSuite) TestRmForceRemoveRunningContainer(c *check.C) {
	testRequires(c, DaemonIsLinux)
	createRunningContainer(c, "foo")

	// Stop then remove with -s
	dockerCmd(c, "rm", "-f", "foo")
}

func (s *DockerSuite) TestRmContainerOrphaning(c *check.C) {
	testRequires(c, DaemonIsLinux)
	dockerfile1 := `FROM busybox:latest
	ENTRYPOINT ["/bin/true"]`
	img := "test-container-orphaning"
	dockerfile2 := `FROM busybox:latest
	ENTRYPOINT ["/bin/true"]
	MAINTAINER Integration Tests`

	// build first dockerfile
	img1, err := buildImage(img, dockerfile1, true)
	c.Assert(err, check.IsNil, check.Commentf("Could not build image %s", img))
	// run container on first image
	dockerCmd(c, "run", img)
	// rebuild dockerfile with a small addition at the end
	_, err = buildImage(img, dockerfile2, true)
	c.Assert(err, check.IsNil, check.Commentf("Could not rebuild image %s", img))
	// try to remove the image, should not error out.
	out, _, err := dockerCmdWithError("rmi", img)
	c.Assert(err, check.IsNil, check.Commentf("Expected to removing the image, but failed: %s", out))

	// check if we deleted the first image
	out, _ = dockerCmd(c, "images", "-q", "--no-trunc")
	c.Assert(out, checker.Contains, img1, check.Commentf("Orphaned container (could not find %q in docker images): %s", img1, out))

}

func (s *DockerSuite) TestRmInvalidContainer(c *check.C) {
	if out, _, err := dockerCmdWithError("rm", "unknown"); err == nil {
		c.Fatal("Expected error on rm unknown container, got none")
	} else if !strings.Contains(out, "Failed to remove container") {
		c.Fatalf("Expected output to contain 'Failed to remove container', got %q", out)
	}
}

func createRunningContainer(c *check.C, name string) {
	dockerCmd(c, "run", "-dt", "--name", name, "busybox", "top")
}
<commit_msg>Windows CI: Port docker_cli_rm_test.go<commit_after>package main

import (
	"io/ioutil"
	"os"
	"strings"

	"github.com/docker/docker/pkg/integration/checker"
	"github.com/go-check/check"
)

func (s *DockerSuite) TestRmContainerWithRemovedVolume(c *check.C) {
	testRequires(c, SameHostDaemon)

	prefix := ""
	slash := "/"
	if daemonPlatform == "windows" {
		prefix = "c:"
		slash = `\`
	}

	tempDir, err := ioutil.TempDir("", "test-rm-container-with-removed-volume-")
	if err != nil {
		c.Fatalf("failed to create temporary directory: %s", tempDir)
	}
	defer os.RemoveAll(tempDir)

	dockerCmd(c, "run", "--name", "losemyvolumes", "-v", tempDir+":"+prefix+slash+"test", "busybox", "true")

	err = os.RemoveAll(tempDir)
	c.Assert(err, check.IsNil)

	dockerCmd(c, "rm", "-v", "losemyvolumes")
}

func (s *DockerSuite) TestRmContainerWithVolume(c *check.C) {
	prefix := ""
	slash := "/"
	if daemonPlatform == "windows" {
		prefix = "c:"
		slash = `\`
	}

	dockerCmd(c, "run", "--name", "foo", "-v", prefix+slash+"srv", "busybox", "true")

	dockerCmd(c, "rm", "-v", "foo")
}

func (s *DockerSuite) TestRmContainerRunning(c *check.C) {
	createRunningContainer(c, "foo")

	_, _, err := dockerCmdWithError("rm", "foo")
	c.Assert(err, checker.NotNil, check.Commentf("Expected error, can't rm a running container"))
}

func (s *DockerSuite) TestRmContainerForceRemoveRunning(c *check.C) {
	createRunningContainer(c, "foo")

	// Stop then remove with -s
	dockerCmd(c, "rm", "-f", "foo")
}

func (s *DockerSuite) TestRmContainerOrphaning(c *check.C) {
	dockerfile1 := `FROM busybox:latest
	ENTRYPOINT ["true"]`
	img := "test-container-orphaning"
	dockerfile2 := `FROM busybox:latest
	ENTRYPOINT ["true"]
	MAINTAINER Integration Tests`

	// build first dockerfile
	img1, err := buildImage(img, dockerfile1, true)
	c.Assert(err, check.IsNil, check.Commentf("Could not build image %s", img))
	// run container on first image
	dockerCmd(c, "run", img)
	// rebuild dockerfile with a small addition at the end
	_, err = buildImage(img, dockerfile2, true)
	c.Assert(err, check.IsNil, check.Commentf("Could not rebuild image %s", img))
	// try to remove the image, should not error out.
	out, _, err := dockerCmdWithError("rmi", img)
	c.Assert(err, check.IsNil, check.Commentf("Expected to removing the image, but failed: %s", out))

	// check if we deleted the first image
	out, _ = dockerCmd(c, "images", "-q", "--no-trunc")
	c.Assert(out, checker.Contains, img1, check.Commentf("Orphaned container (could not find %q in docker images): %s", img1, out))

}

func (s *DockerSuite) TestRmInvalidContainer(c *check.C) {
	if out, _, err := dockerCmdWithError("rm", "unknown"); err == nil {
		c.Fatal("Expected error on rm unknown container, got none")
	} else if !strings.Contains(out, "Failed to remove container") {
		c.Fatalf("Expected output to contain 'Failed to remove container', got %q", out)
	}
}

func createRunningContainer(c *check.C, name string) {
	runSleepingContainer(c, "-dt", "--name", name)
}
<|endoftext|>


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

package source

import (
	"context"
	"fmt"
	"go/ast"
	"go/token"
	"go/types"
	"path/filepath"

	"golang.org/x/tools/go/ast/astutil"
	"golang.org/x/tools/internal/event"
	"golang.org/x/tools/internal/lsp/debug/tag"
	"golang.org/x/tools/internal/lsp/protocol"
	"golang.org/x/tools/internal/span"
	errors "golang.org/x/xerrors"
)

// PrepareCallHierarchy returns an array of CallHierarchyItem for a file and the position within the file.
func PrepareCallHierarchy(ctx context.Context, snapshot Snapshot, fh FileHandle, pos protocol.Position) ([]protocol.CallHierarchyItem, error) {
	ctx, done := event.Start(ctx, "source.PrepareCallHierarchy")
	defer done()

	identifier, err := Identifier(ctx, snapshot, fh, pos)
	if err != nil {
		if errors.Is(err, ErrNoIdentFound) || errors.Is(err, errNoObjectFound) {
			return nil, nil
		}
		return nil, err
	}

	if _, ok := identifier.Declaration.obj.Type().Underlying().(*types.Signature); !ok {
		return nil, nil
	}

	if len(identifier.Declaration.MappedRange) == 0 {
		return nil, nil
	}
	declMappedRange := identifier.Declaration.MappedRange[0]
	rng, err := declMappedRange.Range()
	if err != nil {
		return nil, err
	}

	callHierarchyItem := protocol.CallHierarchyItem{
		Name:           identifier.Name,
		Kind:           protocol.Function,
		Tags:           []protocol.SymbolTag{},
		Detail:         fmt.Sprintf("%s • %s", identifier.Declaration.obj.Pkg().Path(), filepath.Base(declMappedRange.URI().Filename())),
		URI:            protocol.DocumentURI(declMappedRange.URI()),
		Range:          rng,
		SelectionRange: rng,
	}
	return []protocol.CallHierarchyItem{callHierarchyItem}, nil
}

// IncomingCalls returns an array of CallHierarchyIncomingCall for a file and the position within the file.
func IncomingCalls(ctx context.Context, snapshot Snapshot, fh FileHandle, pos protocol.Position) ([]protocol.CallHierarchyIncomingCall, error) {
	ctx, done := event.Start(ctx, "source.IncomingCalls")
	defer done()

	refs, err := References(ctx, snapshot, fh, pos, false)
	if err != nil {
		if errors.Is(err, ErrNoIdentFound) || errors.Is(err, errNoObjectFound) {
			return nil, nil
		}
		return nil, err
	}

	return toProtocolIncomingCalls(ctx, snapshot, refs)
}

// toProtocolIncomingCalls returns an array of protocol.CallHierarchyIncomingCall for ReferenceInfo's.
// References inside same enclosure are assigned to the same enclosing function.
func toProtocolIncomingCalls(ctx context.Context, snapshot Snapshot, refs []*ReferenceInfo) ([]protocol.CallHierarchyIncomingCall, error) {
	// an enclosing node could have multiple calls to a reference, we only show the enclosure
	// once in the result but highlight all calls using FromRanges (ranges at which the calls occur)
	var incomingCalls = map[protocol.Location]*protocol.CallHierarchyIncomingCall{}
	for _, ref := range refs {
		refRange, err := ref.Range()
		if err != nil {
			return nil, err
		}

		callItem, err := enclosingNodeCallItem(snapshot, ref.pkg, ref.URI(), ref.ident.NamePos)
		if err != nil {
			event.Error(ctx, "error getting enclosing node", err, tag.Method.Of(ref.Name))
			continue
		}
		loc := protocol.Location{
			URI:   callItem.URI,
			Range: callItem.Range,
		}

		if incomingCall, ok := incomingCalls[loc]; ok {
			incomingCall.FromRanges = append(incomingCall.FromRanges, refRange)
			continue
		}
		incomingCalls[loc] = &protocol.CallHierarchyIncomingCall{
			From:       callItem,
			FromRanges: []protocol.Range{refRange},
		}
	}

	incomingCallItems := make([]protocol.CallHierarchyIncomingCall, 0, len(incomingCalls))
	for _, callItem := range incomingCalls {
		incomingCallItems = append(incomingCallItems, *callItem)
	}
	return incomingCallItems, nil
}

// enclosingNodeCallItem creates a CallHierarchyItem representing the function call at pos
func enclosingNodeCallItem(snapshot Snapshot, pkg Package, uri span.URI, pos token.Pos) (protocol.CallHierarchyItem, error) {
	pgf, err := pkg.File(uri)
	if err != nil {
		return protocol.CallHierarchyItem{}, err
	}

	var funcDecl *ast.FuncDecl
	var funcLit *ast.FuncLit // innermost function literal
	var litCount int
	// Find the enclosing function, if any, and the number of func literals in between.
	path, _ := astutil.PathEnclosingInterval(pgf.File, pos, pos)
outer:
	for _, node := range path {
		switch n := node.(type) {
		case *ast.FuncDecl:
			funcDecl = n
			break outer
		case *ast.FuncLit:
			litCount++
			if litCount > 1 {
				continue
			}
			funcLit = n
		}
	}

	nameIdent := path[len(path)-1].(*ast.File).Name
	kind := protocol.Package
	if funcDecl != nil {
		nameIdent = funcDecl.Name
		kind = protocol.Function
	}

	nameStart, nameEnd := nameIdent.NamePos, nameIdent.NamePos+token.Pos(len(nameIdent.Name))
	if funcLit != nil {
		nameStart, nameEnd = funcLit.Type.Func, funcLit.Type.Params.Pos()
		kind = protocol.Function
	}
	rng, err := NewMappedRange(snapshot.FileSet(), pgf.Mapper, nameStart, nameEnd).Range()
	if err != nil {
		return protocol.CallHierarchyItem{}, err
	}

	name := nameIdent.Name
	for i := 0; i < litCount; i++ {
		name += ".func()"
	}

	return protocol.CallHierarchyItem{
		Name:           name,
		Kind:           kind,
		Tags:           []protocol.SymbolTag{},
		Detail:         fmt.Sprintf("%s • %s", pkg.PkgPath(), filepath.Base(uri.Filename())),
		URI:            protocol.DocumentURI(uri),
		Range:          rng,
		SelectionRange: rng,
	}, nil
}

// OutgoingCalls returns an array of CallHierarchyOutgoingCall for a file and the position within the file.
func OutgoingCalls(ctx context.Context, snapshot Snapshot, fh FileHandle, pos protocol.Position) ([]protocol.CallHierarchyOutgoingCall, error) {
	ctx, done := event.Start(ctx, "source.OutgoingCalls")
	defer done()

	identifier, err := Identifier(ctx, snapshot, fh, pos)
	if err != nil {
		if errors.Is(err, ErrNoIdentFound) || errors.Is(err, errNoObjectFound) {
			return nil, nil
		}
		return nil, err
	}

	if _, ok := identifier.Declaration.obj.Type().Underlying().(*types.Signature); !ok {
		return nil, nil
	}

	if len(identifier.Declaration.MappedRange) == 0 {
		return nil, nil
	}
	declMappedRange := identifier.Declaration.MappedRange[0]
	callExprs, err := collectCallExpressions(snapshot.FileSet(), declMappedRange.m, identifier.Declaration.node)
	if err != nil {
		return nil, err
	}

	return toProtocolOutgoingCalls(ctx, snapshot, fh, callExprs)
}

// collectCallExpressions collects call expression ranges inside a function.
func collectCallExpressions(fset *token.FileSet, mapper *protocol.ColumnMapper, node ast.Node) ([]protocol.Range, error) {
	type callPos struct {
		start, end token.Pos
	}
	callPositions := []callPos{}

	ast.Inspect(node, func(n ast.Node) bool {
		if call, ok := n.(*ast.CallExpr); ok {
			var start, end token.Pos
			switch n := call.Fun.(type) {
			case *ast.SelectorExpr:
				start, end = n.Sel.NamePos, call.Lparen
			case *ast.Ident:
				start, end = n.NamePos, call.Lparen
			default:
				// ignore any other kind of call expressions
				// for ex: direct function literal calls since that's not an 'outgoing' call
				return false
			}
			callPositions = append(callPositions, callPos{start: start, end: end})
		}
		return true
	})

	callRanges := []protocol.Range{}
	for _, call := range callPositions {
		callRange, err := NewMappedRange(fset, mapper, call.start, call.end).Range()
		if err != nil {
			return nil, err
		}
		callRanges = append(callRanges, callRange)
	}
	return callRanges, nil
}

// toProtocolOutgoingCalls returns an array of protocol.CallHierarchyOutgoingCall for ast call expressions.
// Calls to the same function are assigned to the same declaration.
func toProtocolOutgoingCalls(ctx context.Context, snapshot Snapshot, fh FileHandle, callRanges []protocol.Range) ([]protocol.CallHierarchyOutgoingCall, error) {
	// multiple calls could be made to the same function
	var outgoingCalls = map[ast.Node]*protocol.CallHierarchyOutgoingCall{}
	for _, callRange := range callRanges {
		identifier, err := Identifier(ctx, snapshot, fh, callRange.Start)
		if err != nil {
			if errors.Is(err, ErrNoIdentFound) || errors.Is(err, errNoObjectFound) {
				continue
			}
			return nil, err
		}

		// ignore calls to builtin functions
		if identifier.Declaration.obj.Pkg() == nil {
			continue
		}

		if outgoingCall, ok := outgoingCalls[identifier.Declaration.node]; ok {
			outgoingCall.FromRanges = append(outgoingCall.FromRanges, callRange)
			continue
		}

		if len(identifier.Declaration.MappedRange) == 0 {
			continue
		}
		declMappedRange := identifier.Declaration.MappedRange[0]
		rng, err := declMappedRange.Range()
		if err != nil {
			return nil, err
		}

		outgoingCalls[identifier.Declaration.node] = &protocol.CallHierarchyOutgoingCall{
			To: protocol.CallHierarchyItem{
				Name:           identifier.Name,
				Kind:           protocol.Function,
				Tags:           []protocol.SymbolTag{},
				Detail:         fmt.Sprintf("%s • %s", identifier.Declaration.obj.Pkg().Path(), filepath.Base(declMappedRange.URI().Filename())),
				URI:            protocol.DocumentURI(declMappedRange.URI()),
				Range:          rng,
				SelectionRange: rng,
			},
			FromRanges: []protocol.Range{callRange},
		}
	}

	outgoingCallItems := make([]protocol.CallHierarchyOutgoingCall, 0, len(outgoingCalls))
	for _, callItem := range outgoingCalls {
		outgoingCallItems = append(outgoingCallItems, *callItem)
	}
	return outgoingCallItems, nil
}
<commit_msg>internal/lsp/source: handle a possible nil pointer in call hierarchy<commit_after>// Copyright 2020 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.

package source

import (
	"context"
	"fmt"
	"go/ast"
	"go/token"
	"go/types"
	"path/filepath"

	"golang.org/x/tools/go/ast/astutil"
	"golang.org/x/tools/internal/event"
	"golang.org/x/tools/internal/lsp/debug/tag"
	"golang.org/x/tools/internal/lsp/protocol"
	"golang.org/x/tools/internal/span"
	errors "golang.org/x/xerrors"
)

// PrepareCallHierarchy returns an array of CallHierarchyItem for a file and the position within the file.
func PrepareCallHierarchy(ctx context.Context, snapshot Snapshot, fh FileHandle, pos protocol.Position) ([]protocol.CallHierarchyItem, error) {
	ctx, done := event.Start(ctx, "source.PrepareCallHierarchy")
	defer done()

	identifier, err := Identifier(ctx, snapshot, fh, pos)
	if err != nil {
		if errors.Is(err, ErrNoIdentFound) || errors.Is(err, errNoObjectFound) {
			return nil, nil
		}
		return nil, err
	}
	// The identifier can be nil if it is an import spec.
	if identifier == nil {
		return nil, nil
	}

	if _, ok := identifier.Declaration.obj.Type().Underlying().(*types.Signature); !ok {
		return nil, nil
	}

	if len(identifier.Declaration.MappedRange) == 0 {
		return nil, nil
	}
	declMappedRange := identifier.Declaration.MappedRange[0]
	rng, err := declMappedRange.Range()
	if err != nil {
		return nil, err
	}

	callHierarchyItem := protocol.CallHierarchyItem{
		Name:           identifier.Name,
		Kind:           protocol.Function,
		Tags:           []protocol.SymbolTag{},
		Detail:         fmt.Sprintf("%s • %s", identifier.Declaration.obj.Pkg().Path(), filepath.Base(declMappedRange.URI().Filename())),
		URI:            protocol.DocumentURI(declMappedRange.URI()),
		Range:          rng,
		SelectionRange: rng,
	}
	return []protocol.CallHierarchyItem{callHierarchyItem}, nil
}

// IncomingCalls returns an array of CallHierarchyIncomingCall for a file and the position within the file.
func IncomingCalls(ctx context.Context, snapshot Snapshot, fh FileHandle, pos protocol.Position) ([]protocol.CallHierarchyIncomingCall, error) {
	ctx, done := event.Start(ctx, "source.IncomingCalls")
	defer done()

	refs, err := References(ctx, snapshot, fh, pos, false)
	if err != nil {
		if errors.Is(err, ErrNoIdentFound) || errors.Is(err, errNoObjectFound) {
			return nil, nil
		}
		return nil, err
	}

	return toProtocolIncomingCalls(ctx, snapshot, refs)
}

// toProtocolIncomingCalls returns an array of protocol.CallHierarchyIncomingCall for ReferenceInfo's.
// References inside same enclosure are assigned to the same enclosing function.
func toProtocolIncomingCalls(ctx context.Context, snapshot Snapshot, refs []*ReferenceInfo) ([]protocol.CallHierarchyIncomingCall, error) {
	// an enclosing node could have multiple calls to a reference, we only show the enclosure
	// once in the result but highlight all calls using FromRanges (ranges at which the calls occur)
	var incomingCalls = map[protocol.Location]*protocol.CallHierarchyIncomingCall{}
	for _, ref := range refs {
		refRange, err := ref.Range()
		if err != nil {
			return nil, err
		}

		callItem, err := enclosingNodeCallItem(snapshot, ref.pkg, ref.URI(), ref.ident.NamePos)
		if err != nil {
			event.Error(ctx, "error getting enclosing node", err, tag.Method.Of(ref.Name))
			continue
		}
		loc := protocol.Location{
			URI:   callItem.URI,
			Range: callItem.Range,
		}

		if incomingCall, ok := incomingCalls[loc]; ok {
			incomingCall.FromRanges = append(incomingCall.FromRanges, refRange)
			continue
		}
		incomingCalls[loc] = &protocol.CallHierarchyIncomingCall{
			From:       callItem,
			FromRanges: []protocol.Range{refRange},
		}
	}

	incomingCallItems := make([]protocol.CallHierarchyIncomingCall, 0, len(incomingCalls))
	for _, callItem := range incomingCalls {
		incomingCallItems = append(incomingCallItems, *callItem)
	}
	return incomingCallItems, nil
}

// enclosingNodeCallItem creates a CallHierarchyItem representing the function call at pos
func enclosingNodeCallItem(snapshot Snapshot, pkg Package, uri span.URI, pos token.Pos) (protocol.CallHierarchyItem, error) {
	pgf, err := pkg.File(uri)
	if err != nil {
		return protocol.CallHierarchyItem{}, err
	}

	var funcDecl *ast.FuncDecl
	var funcLit *ast.FuncLit // innermost function literal
	var litCount int
	// Find the enclosing function, if any, and the number of func literals in between.
	path, _ := astutil.PathEnclosingInterval(pgf.File, pos, pos)
outer:
	for _, node := range path {
		switch n := node.(type) {
		case *ast.FuncDecl:
			funcDecl = n
			break outer
		case *ast.FuncLit:
			litCount++
			if litCount > 1 {
				continue
			}
			funcLit = n
		}
	}

	nameIdent := path[len(path)-1].(*ast.File).Name
	kind := protocol.Package
	if funcDecl != nil {
		nameIdent = funcDecl.Name
		kind = protocol.Function
	}

	nameStart, nameEnd := nameIdent.NamePos, nameIdent.NamePos+token.Pos(len(nameIdent.Name))
	if funcLit != nil {
		nameStart, nameEnd = funcLit.Type.Func, funcLit.Type.Params.Pos()
		kind = protocol.Function
	}
	rng, err := NewMappedRange(snapshot.FileSet(), pgf.Mapper, nameStart, nameEnd).Range()
	if err != nil {
		return protocol.CallHierarchyItem{}, err
	}

	name := nameIdent.Name
	for i := 0; i < litCount; i++ {
		name += ".func()"
	}

	return protocol.CallHierarchyItem{
		Name:           name,
		Kind:           kind,
		Tags:           []protocol.SymbolTag{},
		Detail:         fmt.Sprintf("%s • %s", pkg.PkgPath(), filepath.Base(uri.Filename())),
		URI:            protocol.DocumentURI(uri),
		Range:          rng,
		SelectionRange: rng,
	}, nil
}

// OutgoingCalls returns an array of CallHierarchyOutgoingCall for a file and the position within the file.
func OutgoingCalls(ctx context.Context, snapshot Snapshot, fh FileHandle, pos protocol.Position) ([]protocol.CallHierarchyOutgoingCall, error) {
	ctx, done := event.Start(ctx, "source.OutgoingCalls")
	defer done()

	identifier, err := Identifier(ctx, snapshot, fh, pos)
	if err != nil {
		if errors.Is(err, ErrNoIdentFound) || errors.Is(err, errNoObjectFound) {
			return nil, nil
		}
		return nil, err
	}

	if _, ok := identifier.Declaration.obj.Type().Underlying().(*types.Signature); !ok {
		return nil, nil
	}

	if len(identifier.Declaration.MappedRange) == 0 {
		return nil, nil
	}
	declMappedRange := identifier.Declaration.MappedRange[0]
	callExprs, err := collectCallExpressions(snapshot.FileSet(), declMappedRange.m, identifier.Declaration.node)
	if err != nil {
		return nil, err
	}

	return toProtocolOutgoingCalls(ctx, snapshot, fh, callExprs)
}

// collectCallExpressions collects call expression ranges inside a function.
func collectCallExpressions(fset *token.FileSet, mapper *protocol.ColumnMapper, node ast.Node) ([]protocol.Range, error) {
	type callPos struct {
		start, end token.Pos
	}
	callPositions := []callPos{}

	ast.Inspect(node, func(n ast.Node) bool {
		if call, ok := n.(*ast.CallExpr); ok {
			var start, end token.Pos
			switch n := call.Fun.(type) {
			case *ast.SelectorExpr:
				start, end = n.Sel.NamePos, call.Lparen
			case *ast.Ident:
				start, end = n.NamePos, call.Lparen
			default:
				// ignore any other kind of call expressions
				// for ex: direct function literal calls since that's not an 'outgoing' call
				return false
			}
			callPositions = append(callPositions, callPos{start: start, end: end})
		}
		return true
	})

	callRanges := []protocol.Range{}
	for _, call := range callPositions {
		callRange, err := NewMappedRange(fset, mapper, call.start, call.end).Range()
		if err != nil {
			return nil, err
		}
		callRanges = append(callRanges, callRange)
	}
	return callRanges, nil
}

// toProtocolOutgoingCalls returns an array of protocol.CallHierarchyOutgoingCall for ast call expressions.
// Calls to the same function are assigned to the same declaration.
func toProtocolOutgoingCalls(ctx context.Context, snapshot Snapshot, fh FileHandle, callRanges []protocol.Range) ([]protocol.CallHierarchyOutgoingCall, error) {
	// multiple calls could be made to the same function
	var outgoingCalls = map[ast.Node]*protocol.CallHierarchyOutgoingCall{}
	for _, callRange := range callRanges {
		identifier, err := Identifier(ctx, snapshot, fh, callRange.Start)
		if err != nil {
			if errors.Is(err, ErrNoIdentFound) || errors.Is(err, errNoObjectFound) {
				continue
			}
			return nil, err
		}

		// ignore calls to builtin functions
		if identifier.Declaration.obj.Pkg() == nil {
			continue
		}

		if outgoingCall, ok := outgoingCalls[identifier.Declaration.node]; ok {
			outgoingCall.FromRanges = append(outgoingCall.FromRanges, callRange)
			continue
		}

		if len(identifier.Declaration.MappedRange) == 0 {
			continue
		}
		declMappedRange := identifier.Declaration.MappedRange[0]
		rng, err := declMappedRange.Range()
		if err != nil {
			return nil, err
		}

		outgoingCalls[identifier.Declaration.node] = &protocol.CallHierarchyOutgoingCall{
			To: protocol.CallHierarchyItem{
				Name:           identifier.Name,
				Kind:           protocol.Function,
				Tags:           []protocol.SymbolTag{},
				Detail:         fmt.Sprintf("%s • %s", identifier.Declaration.obj.Pkg().Path(), filepath.Base(declMappedRange.URI().Filename())),
				URI:            protocol.DocumentURI(declMappedRange.URI()),
				Range:          rng,
				SelectionRange: rng,
			},
			FromRanges: []protocol.Range{callRange},
		}
	}

	outgoingCallItems := make([]protocol.CallHierarchyOutgoingCall, 0, len(outgoingCalls))
	for _, callItem := range outgoingCalls {
		outgoingCallItems = append(outgoingCallItems, *callItem)
	}
	return outgoingCallItems, nil
}
<|endoftext|>


<commit_before>package testkeyshare

import (
	"context"
	"encoding/base64"
	"net/http"
	"path/filepath"
	"testing"

	"github.com/go-chi/chi"
	irma "github.com/privacybydesign/irmago"
	"github.com/privacybydesign/irmago/internal/keysharecore"
	"github.com/privacybydesign/irmago/internal/test"
	"github.com/privacybydesign/irmago/server"
	"github.com/privacybydesign/irmago/server/keyshare/keyshareserver"
	"github.com/sirupsen/logrus"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

var keyshareServ *http.Server

func StartKeyshareServer(t *testing.T, l *logrus.Logger) {
	db := keyshareserver.NewMemoryDatabase()
	err := db.NewUser(&keyshareserver.KeyshareUser{
		Username: "",
		Coredata: keysharecore.EncryptedKeysharePacket{},
	})
	require.NoError(t, err)
	var ep keysharecore.EncryptedKeysharePacket
	p, err := base64.StdEncoding.DecodeString("YWJjZK4w5SC+7D4lDrhiJGvB1iwxSeF90dGGPoGqqG7g3ivbfHibOdkKoOTZPbFlttBzn2EJgaEsL24Re8OWWWw5pd31/GCd14RXcb9Wy2oWhbr0pvJDLpIxXZt/qiQC0nJiIAYWLGZOdj5o0irDfqP1CSfw3IoKkVEl4lHRj0LCeINJIOpEfGlFtl4DHlWu8SMQFV1AIm3Gv64XzGncdkclVd41ti7cicBrcK8N2u9WvY/jCS4/Lxa2syp/O4IY")
	require.NoError(t, err)
	copy(ep[:], p)
	err = db.NewUser(&keyshareserver.KeyshareUser{
		Username: "testusername",
		Coredata: ep,
	})
	require.NoError(t, err)

	testdataPath := test.FindTestdataFolder(t)
	s, err := keyshareserver.New(&keyshareserver.Configuration{
		Configuration: &server.Configuration{
			SchemesPath:           filepath.Join(testdataPath, "irma_configuration"),
			IssuerPrivateKeysPath: filepath.Join(testdataPath, "privatekeys"),
			Logger:                l,
		},
		KeyshareURL:           "http://localhost:8080/irma_keyshare_server/api/v1/",
		DB:                    db,
		JwtKeyID:              0,
		JwtPrivateKeyFile:     filepath.Join(testdataPath, "jwtkeys", "kss-sk.pem"),
		StoragePrimaryKeyFile: filepath.Join(testdataPath, "keyshareStorageTestkey"),
		KeyshareAttribute:     irma.NewAttributeTypeIdentifier("test.test.mijnirma.email"),
	})
	require.NoError(t, err)

	r := chi.NewRouter()
	r.Mount("/irma_keyshare_server/api/v1/", s.Handler())

	keyshareServ = &http.Server{
		Addr:    "localhost:8080",
		Handler: r,
	}

	go func() {
		err := keyshareServ.ListenAndServe()
		if err == http.ErrServerClosed {
			err = nil
		}
		assert.NoError(t, err)
	}()
}

func StopKeyshareServer(t *testing.T) {
	err := keyshareServ.Shutdown(context.Background())
	assert.NoError(t, err)
}
<commit_msg>test: remove removed URL field from keyshare server configuration used in session tests<commit_after>package testkeyshare

import (
	"context"
	"encoding/base64"
	"net/http"
	"path/filepath"
	"testing"

	"github.com/go-chi/chi"
	irma "github.com/privacybydesign/irmago"
	"github.com/privacybydesign/irmago/internal/keysharecore"
	"github.com/privacybydesign/irmago/internal/test"
	"github.com/privacybydesign/irmago/server"
	"github.com/privacybydesign/irmago/server/keyshare/keyshareserver"
	"github.com/sirupsen/logrus"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

var keyshareServ *http.Server

func StartKeyshareServer(t *testing.T, l *logrus.Logger) {
	db := keyshareserver.NewMemoryDatabase()
	err := db.NewUser(&keyshareserver.KeyshareUser{
		Username: "",
		Coredata: keysharecore.EncryptedKeysharePacket{},
	})
	require.NoError(t, err)
	var ep keysharecore.EncryptedKeysharePacket
	p, err := base64.StdEncoding.DecodeString("YWJjZK4w5SC+7D4lDrhiJGvB1iwxSeF90dGGPoGqqG7g3ivbfHibOdkKoOTZPbFlttBzn2EJgaEsL24Re8OWWWw5pd31/GCd14RXcb9Wy2oWhbr0pvJDLpIxXZt/qiQC0nJiIAYWLGZOdj5o0irDfqP1CSfw3IoKkVEl4lHRj0LCeINJIOpEfGlFtl4DHlWu8SMQFV1AIm3Gv64XzGncdkclVd41ti7cicBrcK8N2u9WvY/jCS4/Lxa2syp/O4IY")
	require.NoError(t, err)
	copy(ep[:], p)
	err = db.NewUser(&keyshareserver.KeyshareUser{
		Username: "testusername",
		Coredata: ep,
	})
	require.NoError(t, err)

	testdataPath := test.FindTestdataFolder(t)
	s, err := keyshareserver.New(&keyshareserver.Configuration{
		Configuration: &server.Configuration{
			SchemesPath:           filepath.Join(testdataPath, "irma_configuration"),
			IssuerPrivateKeysPath: filepath.Join(testdataPath, "privatekeys"),
			Logger:                l,
		},
		DB:                    db,
		JwtKeyID:              0,
		JwtPrivateKeyFile:     filepath.Join(testdataPath, "jwtkeys", "kss-sk.pem"),
		StoragePrimaryKeyFile: filepath.Join(testdataPath, "keyshareStorageTestkey"),
		KeyshareAttribute:     irma.NewAttributeTypeIdentifier("test.test.mijnirma.email"),
	})
	require.NoError(t, err)

	r := chi.NewRouter()
	r.Mount("/irma_keyshare_server/api/v1/", s.Handler())

	keyshareServ = &http.Server{
		Addr:    "localhost:8080",
		Handler: r,
	}

	go func() {
		err := keyshareServ.ListenAndServe()
		if err == http.ErrServerClosed {
			err = nil
		}
		assert.NoError(t, err)
	}()
}

func StopKeyshareServer(t *testing.T) {
	err := keyshareServ.Shutdown(context.Background())
	assert.NoError(t, err)
}
<|endoftext|>


<commit_before>package server

import (
	"context"
	"net/http"

	"github.com/rs/zerolog"

	"github.com/gilcrest/go-api-basic/domain/app"
	"github.com/gilcrest/go-api-basic/domain/audit"
	"github.com/gilcrest/go-api-basic/domain/user"
	"github.com/gilcrest/go-api-basic/service"
)

// CreateMovieService creates a Movie
type CreateMovieService interface {
	Create(ctx context.Context, r *service.CreateMovieRequest, adt audit.Audit) (service.MovieResponse, error)
}

// UpdateMovieService is a service for updating a Movie
type UpdateMovieService interface {
	Update(ctx context.Context, r *service.UpdateMovieRequest, adt audit.Audit) (service.MovieResponse, error)
}

// DeleteMovieService is a service for deleting a Movie
type DeleteMovieService interface {
	Delete(ctx context.Context, extlID string) (service.DeleteMovieResponse, error)
}

// FindMovieService interface reads a Movie form the database
type FindMovieService interface {
	FindMovieByID(ctx context.Context, extlID string) (service.MovieResponse, error)
	FindAllMovies(ctx context.Context) ([]service.MovieResponse, error)
}

// OrgService manages the retrieval and manipulation of an Org
type OrgService interface {
	Create(ctx context.Context, r *service.CreateOrgRequest, adt audit.Audit) (service.OrgResponse, error)
	Update(ctx context.Context, r *service.UpdateOrgRequest, adt audit.Audit) (service.OrgResponse, error)
	FindAll(ctx context.Context) ([]service.OrgResponse, error)
	FindByExternalID(ctx context.Context, extlID string) (service.OrgResponse, error)
}

// CreateAppService creates an App
type CreateAppService interface {
	Create(ctx context.Context, r *service.CreateAppRequest, adt audit.Audit) (service.AppResponse, error)
}

// FindAppService retrieves an App
type FindAppService interface {
	// FindAppByAPIKey finds an app given its External ID and determines
	// if the given API key is a valid key for it
	FindAppByAPIKey(ctx context.Context, realm, appExtlID, apiKey string) (app.App, error)
}

// RegisterUserService registers a new user
type RegisterUserService interface {
	SelfRegister(ctx context.Context, adt audit.Audit) error
}

// FindUserService retrieves a User
type FindUserService interface {
	FindUserByOauth2Token(ctx context.Context, params service.FindUserParams) (user.User, error)
}

// AuthorizeService determines whether an app and a user can perform
// an action against a resource
type AuthorizeService interface {
	Authorize(lgr zerolog.Logger, r *http.Request, sub audit.Audit) error
}

// LoggerService reads and updates the logger state
type LoggerService interface {
	Read() service.LoggerResponse
	Update(r *service.LoggerRequest) (service.LoggerResponse, error)
}

// PingService pings the database and responds whether it is up or down
type PingService interface {
	Ping(ctx context.Context, logger zerolog.Logger) service.PingResponse
}

// GenesisService initializes the database with dependent data
type GenesisService interface {
	Seed(ctx context.Context) (service.FullGenesisResponse, error)
}

// Services are used by the application service handlers
type Services struct {
	CreateMovieService  CreateMovieService
	UpdateMovieService  UpdateMovieService
	DeleteMovieService  DeleteMovieService
	FindMovieService    FindMovieService
	OrgService          OrgService
	CreateAppService    CreateAppService
	FindAppService      FindAppService
	RegisterUserService RegisterUserService
	FindUserService     FindUserService
	AuthorizeService    AuthorizeService
	PingService         PingService
	LoggerService       LoggerService
	GenesisService      GenesisService
}
<commit_msg>reorganize and refactor service names and grouping<commit_after>package server

import (
	"context"
	"net/http"

	"github.com/rs/zerolog"

	"github.com/gilcrest/go-api-basic/domain/app"
	"github.com/gilcrest/go-api-basic/domain/audit"
	"github.com/gilcrest/go-api-basic/domain/user"
	"github.com/gilcrest/go-api-basic/service"
)

// CreateMovieService creates a Movie
type CreateMovieService interface {
	Create(ctx context.Context, r *service.CreateMovieRequest, adt audit.Audit) (service.MovieResponse, error)
}

// UpdateMovieService is a service for updating a Movie
type UpdateMovieService interface {
	Update(ctx context.Context, r *service.UpdateMovieRequest, adt audit.Audit) (service.MovieResponse, error)
}

// DeleteMovieService is a service for deleting a Movie
type DeleteMovieService interface {
	Delete(ctx context.Context, extlID string) (service.DeleteMovieResponse, error)
}

// FindMovieService interface reads a Movie form the database
type FindMovieService interface {
	FindMovieByID(ctx context.Context, extlID string) (service.MovieResponse, error)
	FindAllMovies(ctx context.Context) ([]service.MovieResponse, error)
}

// OrgService manages the retrieval and manipulation of an Org
type OrgService interface {
	Create(ctx context.Context, r *service.CreateOrgRequest, adt audit.Audit) (service.OrgResponse, error)
	Update(ctx context.Context, r *service.UpdateOrgRequest, adt audit.Audit) (service.OrgResponse, error)
	Delete(ctx context.Context, extlID string) (service.DeleteResponse, error)
	FindAll(ctx context.Context) ([]service.OrgResponse, error)
	FindByExternalID(ctx context.Context, extlID string) (service.OrgResponse, error)
}

// AppService manages the retrieval and manipulation of an App
type AppService interface {
	Create(ctx context.Context, r *service.CreateAppRequest, adt audit.Audit) (service.AppResponse, error)
	Update(ctx context.Context, r *service.UpdateAppRequest, adt audit.Audit) (service.AppResponse, error)
}

// MiddlewareService retrieves an App
type MiddlewareService interface {
	// FindAppByAPIKey finds an app given its External ID and determines
	// if the given API key is a valid key for it
	FindAppByAPIKey(ctx context.Context, realm, appExtlID, apiKey string) (app.App, error)
	// FindUserByOauth2Token retrieves a User given an Oauth2 token
	FindUserByOauth2Token(ctx context.Context, params service.FindUserParams) (user.User, error)
	// Authorize determines whether an app/user (as part of an Audit
	// struct) can perform an action against a resource
	Authorize(lgr zerolog.Logger, r *http.Request, sub audit.Audit) error
}

// RegisterUserService registers a new user
type RegisterUserService interface {
	SelfRegister(ctx context.Context, adt audit.Audit) error
}

// LoggerService reads and updates the logger state
type LoggerService interface {
	Read() service.LoggerResponse
	Update(r *service.LoggerRequest) (service.LoggerResponse, error)
}

// PingService pings the database and responds whether it is up or down
type PingService interface {
	Ping(ctx context.Context, logger zerolog.Logger) service.PingResponse
}

// GenesisService initializes the database with dependent data
type GenesisService interface {
	Seed(ctx context.Context) (service.FullGenesisResponse, error)
}

// Services are used by the application service handlers
type Services struct {
	CreateMovieService  CreateMovieService
	UpdateMovieService  UpdateMovieService
	DeleteMovieService  DeleteMovieService
	FindMovieService    FindMovieService
	OrgService          OrgService
	AppService          AppService
	RegisterUserService RegisterUserService
	PingService         PingService
	LoggerService       LoggerService
	GenesisService      GenesisService
	MiddlewareService   MiddlewareService
}
<|endoftext|>


<commit_before>package jobsupervisor

import (
	"fmt"
	"path/filepath"
	"sync"
	"time"

	"github.com/cloudfoundry/bosh-agent/internal/github.com/pivotal-golang/clock"
	"github.com/cloudfoundry/bosh-agent/internal/github.com/pivotal/go-smtpd/smtpd"

	boshalert "github.com/cloudfoundry/bosh-agent/agent/alert"
	bosherr "github.com/cloudfoundry/bosh-agent/internal/github.com/cloudfoundry/bosh-utils/errors"
	boshlog "github.com/cloudfoundry/bosh-agent/internal/github.com/cloudfoundry/bosh-utils/logger"
	boshsys "github.com/cloudfoundry/bosh-agent/internal/github.com/cloudfoundry/bosh-utils/system"
	boshmonit "github.com/cloudfoundry/bosh-agent/jobsupervisor/monit"
	boshdir "github.com/cloudfoundry/bosh-agent/settings/directories"
)

const monitJobSupervisorLogTag = "monitJobSupervisor"

type monitJobSupervisor struct {
	fs                    boshsys.FileSystem
	runner                boshsys.CmdRunner
	client                boshmonit.Client
	logger                boshlog.Logger
	dirProvider           boshdir.Provider
	jobFailuresServerPort int
	reloadOptions         MonitReloadOptions
	timeService           clock.Clock
}

type MonitReloadOptions struct {
	// Number of times `monit reload` will be executed
	MaxTries int

	// Number of times monit incarnation will be checked
	// for difference after executing `monit reload`
	MaxCheckTries int

	// Length of time between checking for incarnation difference
	DelayBetweenCheckTries time.Duration
}

func NewMonitJobSupervisor(
	fs boshsys.FileSystem,
	runner boshsys.CmdRunner,
	client boshmonit.Client,
	logger boshlog.Logger,
	dirProvider boshdir.Provider,
	jobFailuresServerPort int,
	reloadOptions MonitReloadOptions,
	timeService clock.Clock,
) JobSupervisor {
	return &monitJobSupervisor{
		fs:                    fs,
		runner:                runner,
		client:                client,
		logger:                logger,
		dirProvider:           dirProvider,
		jobFailuresServerPort: jobFailuresServerPort,
		reloadOptions:         reloadOptions,
		timeService:           timeService,
	}
}

func (m monitJobSupervisor) Reload() error {
	var currentIncarnation int

	oldIncarnation, err := m.getIncarnation()
	if err != nil {
		return bosherr.WrapError(err, "Getting monit incarnation")
	}

	// Monit process could be started in the same second as `monit reload` runs
	// so it's ideal for MaxCheckTries * DelayBetweenCheckTries to be greater than 1 sec
	// because monit incarnation id is just a timestamp with 1 sec resolution.
	for reloadI := 0; reloadI < m.reloadOptions.MaxTries; reloadI++ {
		// Exit code or output cannot be trusted
		_, _, _, err := m.runner.RunCommand("monit", "reload")
		if err != nil {
			m.logger.Error(monitJobSupervisorLogTag, "Failed to reload monit %s", err.Error())
		}

		for checkI := 0; checkI < m.reloadOptions.MaxCheckTries; checkI++ {
			currentIncarnation, err = m.getIncarnation()
			if err != nil {
				return bosherr.WrapError(err, "Getting monit incarnation")
			}

			// Incarnation id can decrease or increase because
			// monit uses time(...) and system time can be changed
			if oldIncarnation != currentIncarnation {
				return nil
			}

			m.logger.Debug(
				monitJobSupervisorLogTag,
				"Waiting for monit to reload: before=%d after=%d",
				oldIncarnation, currentIncarnation,
			)

			time.Sleep(m.reloadOptions.DelayBetweenCheckTries)
		}
	}

	return bosherr.Errorf(
		"Failed to reload monit: before=%d after=%d",
		oldIncarnation, currentIncarnation,
	)
}

func (m monitJobSupervisor) Start() error {
	services, err := m.client.ServicesInGroup("vcap")
	if err != nil {
		return bosherr.WrapError(err, "Getting vcap services")
	}

	for _, service := range services {
		m.logger.Debug(monitJobSupervisorLogTag, "Starting service %s", service)
		err = m.client.StartService(service)
		if err != nil {
			return bosherr.WrapErrorf(err, "Starting service %s", service)
		}
	}

	return nil
}

func (m monitJobSupervisor) Stop() error {
	defer m.logger.HandlePanic("Monit Job Supervisor Stop")
	var group sync.WaitGroup

	services, err := m.client.ServicesInGroup("vcap")
	if err != nil {
		return bosherr.WrapError(err, "Getting vcap services")
	}

	success := []string{}
	failure := []string{}
	stopped := make(chan string)
	errchan := make(chan error)

	stop := func(service string) error {
		m.logger.Debug(monitJobSupervisorLogTag, "Stopping service %s", service)
		return m.client.StopService(service)
	}

	for _, service := range services {
		group.Add(1)

		go func(serviceName string) {
			defer group.Done()

			stopErr := stop(serviceName)
			if stopErr != nil {
				errchan <- stopErr
				return
			}

			stopped <- serviceName
		}(service)
	}

	// Locate within success slice
	wasSuccess := func(serviceName string) bool {
		for _, name := range success {
			if name == serviceName {
				return true
			}
		}
		return false
	}

	for i := 0; i < len(services); i++ {
		select {
		case s := <-stopped:
			success = append(success, s)
			m.logger.Debug(monitJobSupervisorLogTag, "Stopped service %s", s)
		case e := <-errchan:
			return bosherr.WrapErrorf(e, "Stopping service")
		case <-m.timeService.NewTimer(10 * time.Minute).C():
			// Build up failures (not found in success slice)
			// TODO: Find a better way to do this.
			failure = []string{}
			for _, service := range services {
				if !wasSuccess(service) {
					failure = append(failure, service)
				}
			}
			return bosherr.Errorf("Timed out stopping services. Failures: %v", failure)
		}
	}

	group.Wait()

	return nil
}

func (m monitJobSupervisor) Unmonitor() error {
	services, err := m.client.ServicesInGroup("vcap")
	if err != nil {
		return bosherr.WrapError(err, "Getting vcap services")
	}

	for _, service := range services {
		m.logger.Debug(monitJobSupervisorLogTag, "Unmonitoring service %s", service)
		err := m.client.UnmonitorService(service)
		if err != nil {
			return bosherr.WrapErrorf(err, "Unmonitoring service %s", service)
		}
	}

	return nil
}

func (m monitJobSupervisor) Status() (status string) {
	status = "running"
	m.logger.Debug(monitJobSupervisorLogTag, "Getting monit status")
	monitStatus, err := m.client.Status()
	if err != nil {
		status = "unknown"
		return
	}

	for _, service := range monitStatus.ServicesInGroup("vcap") {
		if service.Status == "starting" {
			return "starting"
		}
		if !service.Monitored || service.Status != "running" {
			status = "failing"
		}
	}

	return
}

func (m monitJobSupervisor) getIncarnation() (int, error) {
	monitStatus, err := m.client.Status()
	if err != nil {
		return -1, err
	}

	return monitStatus.GetIncarnation()
}

func (m monitJobSupervisor) AddJob(jobName string, jobIndex int, configPath string) error {
	targetFilename := fmt.Sprintf("%04d_%s.monitrc", jobIndex, jobName)
	targetConfigPath := filepath.Join(m.dirProvider.MonitJobsDir(), targetFilename)

	configContent, err := m.fs.ReadFile(configPath)
	if err != nil {
		return bosherr.WrapError(err, "Reading job config from file")
	}

	err = m.fs.WriteFile(targetConfigPath, configContent)
	if err != nil {
		return bosherr.WrapError(err, "Writing to job config file")
	}

	return nil
}

func (m monitJobSupervisor) RemoveAllJobs() error {
	return m.fs.RemoveAll(m.dirProvider.MonitJobsDir())
}

func (m monitJobSupervisor) MonitorJobFailures(handler JobFailureHandler) (err error) {
	alertHandler := func(smtpd.Connection, smtpd.MailAddress) (env smtpd.Envelope, err error) {
		env = &alertEnvelope{
			new(smtpd.BasicEnvelope),
			handler,
			new(boshalert.MonitAlert),
		}
		return
	}

	serv := &smtpd.Server{
		Addr:      fmt.Sprintf("127.0.0.1:%d", m.jobFailuresServerPort),
		OnNewMail: alertHandler,
	}

	err = serv.ListenAndServe()
	if err != nil {
		err = bosherr.WrapError(err, "Listen for SMTP")
	}
	return
}
<commit_msg>Moves failure slice definition.<commit_after>package jobsupervisor

import (
	"fmt"
	"path/filepath"
	"sync"
	"time"

	"github.com/cloudfoundry/bosh-agent/internal/github.com/pivotal-golang/clock"
	"github.com/cloudfoundry/bosh-agent/internal/github.com/pivotal/go-smtpd/smtpd"

	boshalert "github.com/cloudfoundry/bosh-agent/agent/alert"
	bosherr "github.com/cloudfoundry/bosh-agent/internal/github.com/cloudfoundry/bosh-utils/errors"
	boshlog "github.com/cloudfoundry/bosh-agent/internal/github.com/cloudfoundry/bosh-utils/logger"
	boshsys "github.com/cloudfoundry/bosh-agent/internal/github.com/cloudfoundry/bosh-utils/system"
	boshmonit "github.com/cloudfoundry/bosh-agent/jobsupervisor/monit"
	boshdir "github.com/cloudfoundry/bosh-agent/settings/directories"
)

const monitJobSupervisorLogTag = "monitJobSupervisor"

type monitJobSupervisor struct {
	fs                    boshsys.FileSystem
	runner                boshsys.CmdRunner
	client                boshmonit.Client
	logger                boshlog.Logger
	dirProvider           boshdir.Provider
	jobFailuresServerPort int
	reloadOptions         MonitReloadOptions
	timeService           clock.Clock
}

type MonitReloadOptions struct {
	// Number of times `monit reload` will be executed
	MaxTries int

	// Number of times monit incarnation will be checked
	// for difference after executing `monit reload`
	MaxCheckTries int

	// Length of time between checking for incarnation difference
	DelayBetweenCheckTries time.Duration
}

func NewMonitJobSupervisor(
	fs boshsys.FileSystem,
	runner boshsys.CmdRunner,
	client boshmonit.Client,
	logger boshlog.Logger,
	dirProvider boshdir.Provider,
	jobFailuresServerPort int,
	reloadOptions MonitReloadOptions,
	timeService clock.Clock,
) JobSupervisor {
	return &monitJobSupervisor{
		fs:                    fs,
		runner:                runner,
		client:                client,
		logger:                logger,
		dirProvider:           dirProvider,
		jobFailuresServerPort: jobFailuresServerPort,
		reloadOptions:         reloadOptions,
		timeService:           timeService,
	}
}

func (m monitJobSupervisor) Reload() error {
	var currentIncarnation int

	oldIncarnation, err := m.getIncarnation()
	if err != nil {
		return bosherr.WrapError(err, "Getting monit incarnation")
	}

	// Monit process could be started in the same second as `monit reload` runs
	// so it's ideal for MaxCheckTries * DelayBetweenCheckTries to be greater than 1 sec
	// because monit incarnation id is just a timestamp with 1 sec resolution.
	for reloadI := 0; reloadI < m.reloadOptions.MaxTries; reloadI++ {
		// Exit code or output cannot be trusted
		_, _, _, err := m.runner.RunCommand("monit", "reload")
		if err != nil {
			m.logger.Error(monitJobSupervisorLogTag, "Failed to reload monit %s", err.Error())
		}

		for checkI := 0; checkI < m.reloadOptions.MaxCheckTries; checkI++ {
			currentIncarnation, err = m.getIncarnation()
			if err != nil {
				return bosherr.WrapError(err, "Getting monit incarnation")
			}

			// Incarnation id can decrease or increase because
			// monit uses time(...) and system time can be changed
			if oldIncarnation != currentIncarnation {
				return nil
			}

			m.logger.Debug(
				monitJobSupervisorLogTag,
				"Waiting for monit to reload: before=%d after=%d",
				oldIncarnation, currentIncarnation,
			)

			time.Sleep(m.reloadOptions.DelayBetweenCheckTries)
		}
	}

	return bosherr.Errorf(
		"Failed to reload monit: before=%d after=%d",
		oldIncarnation, currentIncarnation,
	)
}

func (m monitJobSupervisor) Start() error {
	services, err := m.client.ServicesInGroup("vcap")
	if err != nil {
		return bosherr.WrapError(err, "Getting vcap services")
	}

	for _, service := range services {
		m.logger.Debug(monitJobSupervisorLogTag, "Starting service %s", service)
		err = m.client.StartService(service)
		if err != nil {
			return bosherr.WrapErrorf(err, "Starting service %s", service)
		}
	}

	return nil
}

func (m monitJobSupervisor) Stop() error {
	defer m.logger.HandlePanic("Monit Job Supervisor Stop")
	var group sync.WaitGroup

	services, err := m.client.ServicesInGroup("vcap")
	if err != nil {
		return bosherr.WrapError(err, "Getting vcap services")
	}

	success := []string{}
	stopped := make(chan string)
	errchan := make(chan error)

	stop := func(service string) error {
		m.logger.Debug(monitJobSupervisorLogTag, "Stopping service %s", service)
		return m.client.StopService(service)
	}

	for _, service := range services {
		group.Add(1)

		go func(serviceName string) {
			defer group.Done()

			stopErr := stop(serviceName)
			if stopErr != nil {
				errchan <- stopErr
				return
			}

			stopped <- serviceName
		}(service)
	}

	// Locate within success slice
	wasSuccess := func(serviceName string) bool {
		for _, name := range success {
			if name == serviceName {
				return true
			}
		}
		return false
	}

	for i := 0; i < len(services); i++ {
		select {
		case s := <-stopped:
			success = append(success, s)
			m.logger.Debug(monitJobSupervisorLogTag, "Stopped service %s", s)
		case e := <-errchan:
			return bosherr.WrapErrorf(e, "Stopping service")
		case <-m.timeService.NewTimer(10 * time.Minute).C():
			// Build up failures (not found in success slice)
			// TODO: Find a better way to do this.
			failure := []string{}
			for _, service := range services {
				if !wasSuccess(service) {
					failure = append(failure, service)
				}
			}
			return bosherr.Errorf("Timed out stopping services. Failures: %v", failure)
		}
	}

	group.Wait()

	return nil
}

func (m monitJobSupervisor) Unmonitor() error {
	services, err := m.client.ServicesInGroup("vcap")
	if err != nil {
		return bosherr.WrapError(err, "Getting vcap services")
	}

	for _, service := range services {
		m.logger.Debug(monitJobSupervisorLogTag, "Unmonitoring service %s", service)
		err := m.client.UnmonitorService(service)
		if err != nil {
			return bosherr.WrapErrorf(err, "Unmonitoring service %s", service)
		}
	}

	return nil
}

func (m monitJobSupervisor) Status() (status string) {
	status = "running"
	m.logger.Debug(monitJobSupervisorLogTag, "Getting monit status")
	monitStatus, err := m.client.Status()
	if err != nil {
		status = "unknown"
		return
	}

	for _, service := range monitStatus.ServicesInGroup("vcap") {
		if service.Status == "starting" {
			return "starting"
		}
		if !service.Monitored || service.Status != "running" {
			status = "failing"
		}
	}

	return
}

func (m monitJobSupervisor) getIncarnation() (int, error) {
	monitStatus, err := m.client.Status()
	if err != nil {
		return -1, err
	}

	return monitStatus.GetIncarnation()
}

func (m monitJobSupervisor) AddJob(jobName string, jobIndex int, configPath string) error {
	targetFilename := fmt.Sprintf("%04d_%s.monitrc", jobIndex, jobName)
	targetConfigPath := filepath.Join(m.dirProvider.MonitJobsDir(), targetFilename)

	configContent, err := m.fs.ReadFile(configPath)
	if err != nil {
		return bosherr.WrapError(err, "Reading job config from file")
	}

	err = m.fs.WriteFile(targetConfigPath, configContent)
	if err != nil {
		return bosherr.WrapError(err, "Writing to job config file")
	}

	return nil
}

func (m monitJobSupervisor) RemoveAllJobs() error {
	return m.fs.RemoveAll(m.dirProvider.MonitJobsDir())
}

func (m monitJobSupervisor) MonitorJobFailures(handler JobFailureHandler) (err error) {
	alertHandler := func(smtpd.Connection, smtpd.MailAddress) (env smtpd.Envelope, err error) {
		env = &alertEnvelope{
			new(smtpd.BasicEnvelope),
			handler,
			new(boshalert.MonitAlert),
		}
		return
	}

	serv := &smtpd.Server{
		Addr:      fmt.Sprintf("127.0.0.1:%d", m.jobFailuresServerPort),
		OnNewMail: alertHandler,
	}

	err = serv.ListenAndServe()
	if err != nil {
		err = bosherr.WrapError(err, "Listen for SMTP")
	}
	return
}
<|endoftext|>


<commit_before>package service

import (
	"context"
	"strings"
	"time"

	"github.com/google/uuid"
	"github.com/jackc/pgx/v4"

	"github.com/gilcrest/go-api-basic/datastore/orgstore"
	"github.com/gilcrest/go-api-basic/domain/app"
	"github.com/gilcrest/go-api-basic/domain/audit"
	"github.com/gilcrest/go-api-basic/domain/errs"
	"github.com/gilcrest/go-api-basic/domain/org"
	"github.com/gilcrest/go-api-basic/domain/person"
	"github.com/gilcrest/go-api-basic/domain/secure"
	"github.com/gilcrest/go-api-basic/domain/user"
)

const genesisOrgTypeString string = "genesis"

type FullGenesisResponse struct {
	GenesisResponse GenesisResponse `json:"genesis"`
	TestResponse    TestResponse    `json:"test"`
}

// GenesisResponse is the response struct for the genesis org and app
type GenesisResponse struct {
	OrgResponse OrgResponse `json:"org"`
	AppResponse AppResponse `json:"app"`
}

// TestResponse is the response struct for the test org and app
type TestResponse struct {
	OrgResponse OrgResponse `json:"org"`
	AppResponse AppResponse `json:"app"`
}

// GenesisService seeds the database. It should be run only once on initial database setup.
type GenesisService struct {
	Datastorer            Datastorer
	RandomStringGenerator CryptoRandomGenerator
	EncryptionKey         *[32]byte
}

type seedSet struct {
	org   org.Org
	app   app.App
	user  user.User
	audit audit.SimpleAudit
}

// Seed method seeds the database
func (s GenesisService) Seed(ctx context.Context) (FullGenesisResponse, error) {

	var (
		tx  pgx.Tx
		err error
	)

	// ensure the Genesis seed event has not already taken place
	err = genesisHasOccurred(ctx, s.Datastorer.Pool())
	if err != nil {
		return FullGenesisResponse{}, err
	}

	// start db txn using pgxpool
	tx, err = s.Datastorer.BeginTx(ctx)
	if err != nil {
		return FullGenesisResponse{}, err
	}

	var (
		genesisSet seedSet
		testSet    seedSet
		testKind   org.Kind
	)
	genesisSet, testKind, err = s.seedGenesis(ctx, tx)
	if err != nil {
		return FullGenesisResponse{}, err
	}

	testSet, err = s.seedTest(ctx, tx, testKind)
	if err != nil {
		return FullGenesisResponse{}, err
	}

	// commit db txn using pgxpool
	err = s.Datastorer.CommitTx(ctx, tx)
	if err != nil {
		return FullGenesisResponse{}, err
	}

	genesisResponse := GenesisResponse{
		OrgResponse: newOrgResponse(orgAudit{Org: genesisSet.org, SimpleAudit: genesisSet.audit}),
		AppResponse: newAppResponse(genesisSet.app),
	}

	testResponse := TestResponse{
		OrgResponse: newOrgResponse(orgAudit{Org: testSet.org, SimpleAudit: testSet.audit}),
		AppResponse: newAppResponse(testSet.app),
	}

	response := FullGenesisResponse{
		GenesisResponse: genesisResponse,
		TestResponse:    testResponse,
	}

	return response, nil
}

func (s GenesisService) seedGenesis(ctx context.Context, tx pgx.Tx) (seedSet, org.Kind, error) {
	var err error

	// create Org
	o := org.Org{
		ID:          uuid.New(),
		ExternalID:  secure.NewID(),
		Name:        "genesis",
		Description: "The genesis org represents the first organization created in the database and exists purely for the administrative purpose of creating other organizations, apps and users.",
	}

	// initialize App and inject dependent fields
	a := app.App{
		ID:          uuid.New(),
		ExternalID:  secure.NewID(),
		Org:         o,
		Name:        "WOPR",
		Description: "App created as part of Genesis event. To be used solely for creating other apps, orgs and users.",
		APIKeys:     nil,
	}

	keyDeactivation := time.Date(2099, 12, 31, 0, 0, 0, 0, time.UTC)
	err = a.AddNewKey(s.RandomStringGenerator, s.EncryptionKey, keyDeactivation)
	if err != nil {
		return seedSet{}, org.Kind{}, errs.E(errs.Internal, s.Datastorer.RollbackTx(ctx, tx, err))
	}

	pgUser, pgAudit := createPeterGabriel(o, a)
	pcUser, pcAudit := createPhilCollins(o, a)

	// create Genesis org kind
	var genesisKindParams orgstore.CreateOrgKindParams
	genesisKindParams, err = createGenesisOrgKind(ctx, s.Datastorer, tx, pgAudit)
	if err != nil {
		return seedSet{}, org.Kind{}, errs.E(errs.Database, s.Datastorer.RollbackTx(ctx, tx, err))
	}
	o.Kind = org.Kind{
		ID:          genesisKindParams.OrgKindID,
		ExternalID:  genesisKindParams.OrgKindExtlID,
		Description: genesisKindParams.OrgKindDesc,
	}

	// create other org kinds (test, standard)
	var testKindParams orgstore.CreateOrgKindParams
	testKindParams, err = createTestOrgKind(ctx, s.Datastorer, tx, pgAudit)
	if err != nil {
		return seedSet{}, org.Kind{}, errs.E(errs.Database, s.Datastorer.RollbackTx(ctx, tx, err))
	}
	tk := org.Kind{
		ID:          testKindParams.OrgKindID,
		ExternalID:  testKindParams.OrgKindExtlID,
		Description: testKindParams.OrgKindDesc,
	}

	err = createStandardOrgKind(ctx, s.Datastorer, tx, pgAudit)
	if err != nil {
		return seedSet{}, org.Kind{}, errs.E(errs.Database, s.Datastorer.RollbackTx(ctx, tx, err))
	}

	sa := audit.SimpleAudit{
		First: pgAudit,
		Last:  pgAudit,
	}

	// write the Org to the database
	err = createOrgDB(ctx, s.Datastorer, tx, orgAudit{Org: o, SimpleAudit: sa})
	if err != nil {
		return seedSet{}, org.Kind{}, err
	}

	// write the App to the database
	err = createAppDB(ctx, s.Datastorer, tx, a, pgAudit)
	if err != nil {
		return seedSet{}, org.Kind{}, err
	}

	// write Peter Gabriel to the database
	err = createUserDB(ctx, s.Datastorer, tx, pgUser, pgAudit)
	if err != nil {
		return seedSet{}, org.Kind{}, err
	}

	// write Phil Collins to the database
	err = createUserDB(ctx, s.Datastorer, tx, pcUser, pcAudit)
	if err != nil {
		return seedSet{}, org.Kind{}, err
	}

	return seedSet{org: o, app: a, user: pgUser, audit: sa}, tk, nil
}

func createPeterGabriel(o org.Org, a app.App) (user.User, audit.Audit) {
	// Peter Gabriel Person
	pgPrsn := person.Person{
		ID:  uuid.New(),
		Org: o,
	}

	// Peter Gabriel Person Profile
	pgPfl := person.Profile{ID: uuid.New(), Person: pgPrsn}
	pgPfl.FirstName = "Peter"
	pgPfl.LastName = "Gabriel"

	// Peter Gabriel User
	pgUser := user.User{
		ID:       uuid.New(),
		Username: strings.TrimSpace("pgabriel"),
		Org:      o,
		Profile:  pgPfl,
	}

	// Peter Gabriel Audit
	pgAudit := audit.Audit{
		App:    a,
		User:   pgUser,
		Moment: time.Now(),
	}

	return pgUser, pgAudit
}

func createPhilCollins(o org.Org, a app.App) (user.User, audit.Audit) {
	// Peter Gabriel Person
	pcPrsn := person.Person{
		ID:  uuid.New(),
		Org: o,
	}

	// Peter Gabriel Person Profile
	pgPfl := person.Profile{ID: uuid.New(), Person: pcPrsn}
	pgPfl.FirstName = "Phil"
	pgPfl.LastName = "Collins"

	// Peter Gabriel User
	pcUser := user.User{
		ID:       uuid.New(),
		Username: strings.TrimSpace("pcollins"),
		Org:      o,
		Profile:  pgPfl,
	}

	// Peter Gabriel Audit
	pcAudit := audit.Audit{
		App:    a,
		User:   pcUser,
		Moment: time.Now(),
	}

	return pcUser, pcAudit
}

func (s GenesisService) seedTest(ctx context.Context, tx pgx.Tx, k org.Kind) (seedSet, error) {
	var err error

	// create Org
	o := org.Org{
		ID:          uuid.New(),
		ExternalID:  secure.NewID(),
		Name:        "test",
		Description: "The test org is self explanatory",
		Kind:        k,
	}

	// initialize App and inject dependent fields
	a := app.App{
		ID:          uuid.New(),
		ExternalID:  secure.NewID(),
		Org:         o,
		Name:        "test",
		Description: "The test app is self explanatory",
		APIKeys:     nil,
	}

	keyDeactivation := time.Date(2099, 12, 31, 0, 0, 0, 0, time.UTC)
	err = a.AddNewKey(s.RandomStringGenerator, s.EncryptionKey, keyDeactivation)
	if err != nil {
		return seedSet{}, errs.E(errs.Internal, s.Datastorer.RollbackTx(ctx, tx, err))
	}

	// create Person
	prsn := person.Person{
		ID:  uuid.New(),
		Org: o,
	}

	// create Person Profile
	pfl := person.Profile{ID: uuid.New(), Person: prsn}
	pfl.FirstName = "Steve"
	pfl.LastName = "Hackett"

	// create User
	u := user.User{
		ID:       uuid.New(),
		Username: strings.TrimSpace("shackett"),
		Org:      o,
		Profile:  pfl,
	}

	//create Audit
	adt := audit.Audit{
		App:    a,
		User:   u,
		Moment: time.Now(),
	}

	sa := audit.SimpleAudit{
		First: adt,
		Last:  adt,
	}

	// write the Org to the database
	err = createOrgDB(ctx, s.Datastorer, tx, orgAudit{Org: o, SimpleAudit: sa})
	if err != nil {
		return seedSet{}, err
	}

	// write the App to the database
	err = createAppDB(ctx, s.Datastorer, tx, a, adt)
	if err != nil {
		return seedSet{}, err
	}

	// write the User to the database
	err = createUserDB(ctx, s.Datastorer, tx, u, adt)
	if err != nil {
		return seedSet{}, err
	}

	return seedSet{org: o, app: a, user: u, audit: sa}, nil
}

func genesisHasOccurred(ctx context.Context, dbtx orgstore.DBTX) (err error) {
	var (
		existingOrgs         []orgstore.FindOrgsByKindExtlIDRow
		hasGenesisOrgTypeRow = true
		hasGenesisOrgRow     = true
	)

	// validate Genesis records do not exist already
	// first: check org_type
	_, err = orgstore.New(dbtx).FindOrgKindByExtlID(ctx, genesisOrgTypeString)
	if err != nil {
		if err != pgx.ErrNoRows {
			return errs.E(errs.Database, err)
		}
		hasGenesisOrgTypeRow = false
	}

	// last: check org
	existingOrgs, err = orgstore.New(dbtx).FindOrgsByKindExtlID(ctx, genesisOrgTypeString)
	if err != nil {
		return errs.E(errs.Database, err)
	}
	if len(existingOrgs) == 0 {
		hasGenesisOrgRow = false
	}

	if hasGenesisOrgTypeRow || hasGenesisOrgRow {
		return errs.E(errs.Validation, "No prior data should exist when executing Genesis Service")
	}

	return nil
}
<commit_msg>use new app methods; streamline db interactions<commit_after>package service

import (
	"context"
	"fmt"
	"strings"
	"time"

	"github.com/google/uuid"
	"github.com/jackc/pgx/v4"

	"github.com/gilcrest/go-api-basic/datastore"
	"github.com/gilcrest/go-api-basic/datastore/appstore"
	"github.com/gilcrest/go-api-basic/datastore/orgstore"
	"github.com/gilcrest/go-api-basic/domain/app"
	"github.com/gilcrest/go-api-basic/domain/audit"
	"github.com/gilcrest/go-api-basic/domain/errs"
	"github.com/gilcrest/go-api-basic/domain/org"
	"github.com/gilcrest/go-api-basic/domain/person"
	"github.com/gilcrest/go-api-basic/domain/secure"
	"github.com/gilcrest/go-api-basic/domain/user"
)

const genesisOrgTypeString string = "genesis"

// FullGenesisResponse contains both the Genesis response and the Test response
type FullGenesisResponse struct {
	GenesisResponse GenesisResponse `json:"genesis"`
	TestResponse    TestResponse    `json:"test"`
}

// GenesisResponse is the response struct for the genesis org and app
type GenesisResponse struct {
	OrgResponse OrgResponse `json:"org"`
	AppResponse AppResponse `json:"app"`
}

// TestResponse is the response struct for the test org and app
type TestResponse struct {
	OrgResponse OrgResponse `json:"org"`
	AppResponse AppResponse `json:"app"`
}

// GenesisService seeds the database. It should be run only once on initial database setup.
type GenesisService struct {
	Datastorer            Datastorer
	RandomStringGenerator CryptoRandomGenerator
	EncryptionKey         *[32]byte
}

type seedSet struct {
	org   org.Org
	app   app.App
	user  user.User
	audit audit.SimpleAudit
}

// Seed method seeds the database
func (s GenesisService) Seed(ctx context.Context) (FullGenesisResponse, error) {

	var err error

	// ensure the Genesis seed event has not already taken place
	err = genesisHasOccurred(ctx, s.Datastorer.Pool())
	if err != nil {
		return FullGenesisResponse{}, err
	}

	var (
		genesisSet seedSet
		testSet    seedSet
		testKind   org.Kind
	)

	// start db txn using pgxpool
	var tx pgx.Tx
	tx, err = s.Datastorer.BeginTx(ctx)
	if err != nil {
		return FullGenesisResponse{}, err
	}

	// seed Genesis data. As part of this method, the initial org.Kind
	// structs are added to the db. The test kind is returned for use
	// in the seedTest method
	genesisSet, testKind, err = s.seedGenesis(ctx, tx)
	if err != nil {
		return FullGenesisResponse{}, err
	}

	// seed Test data.
	testSet, err = s.seedTest(ctx, tx, testKind)
	if err != nil {
		return FullGenesisResponse{}, err
	}

	// commit db txn using pgxpool
	err = s.Datastorer.CommitTx(ctx, tx)
	if err != nil {
		return FullGenesisResponse{}, err
	}

	genesisResponse := GenesisResponse{
		OrgResponse: newOrgResponse(orgAudit{Org: genesisSet.org, SimpleAudit: genesisSet.audit}),
		AppResponse: newAppResponse(appAudit{App: genesisSet.app, SimpleAudit: genesisSet.audit}),
	}

	testResponse := TestResponse{
		OrgResponse: newOrgResponse(orgAudit{Org: testSet.org, SimpleAudit: testSet.audit}),
		AppResponse: newAppResponse(appAudit{App: testSet.app, SimpleAudit: testSet.audit}),
	}

	response := FullGenesisResponse{
		GenesisResponse: genesisResponse,
		TestResponse:    testResponse,
	}

	return response, nil
}

func (s GenesisService) seedGenesis(ctx context.Context, tx pgx.Tx) (seedSet, org.Kind, error) {
	var err error

	// create Org
	o := org.Org{
		ID:          uuid.New(),
		ExternalID:  secure.NewID(),
		Name:        "genesis",
		Description: "The genesis org represents the first organization created in the database and exists purely for the administrative purpose of creating other organizations, apps and users.",
	}

	// initialize App and inject dependent fields
	a := app.App{
		ID:          uuid.New(),
		ExternalID:  secure.NewID(),
		Org:         o,
		Name:        "WOPR",
		Description: "App created as part of Genesis event. To be used solely for creating other apps, orgs and users.",
		APIKeys:     nil,
	}

	keyDeactivation := time.Date(2099, 12, 31, 0, 0, 0, 0, time.UTC)
	err = a.AddNewKey(s.RandomStringGenerator, s.EncryptionKey, keyDeactivation)
	if err != nil {
		return seedSet{}, org.Kind{}, s.Datastorer.RollbackTx(ctx, tx, errs.E(errs.Internal, err))
	}

	pgUser, pgAudit := createPeterGabriel(o, a)
	pcUser, pcAudit := createPhilCollins(o, a)

	// create Genesis org kind
	var genesisKindParams orgstore.CreateOrgKindParams
	genesisKindParams, err = createGenesisOrgKind(ctx, s.Datastorer, tx, pgAudit)
	if err != nil {
		return seedSet{}, org.Kind{}, s.Datastorer.RollbackTx(ctx, tx, errs.E(errs.Database, err))
	}
	o.Kind = org.Kind{
		ID:          genesisKindParams.OrgKindID,
		ExternalID:  genesisKindParams.OrgKindExtlID,
		Description: genesisKindParams.OrgKindDesc,
	}

	// create other org kinds (test, standard)
	var testKindParams orgstore.CreateOrgKindParams
	testKindParams, err = createTestOrgKind(ctx, s.Datastorer, tx, pgAudit)
	if err != nil {
		return seedSet{}, org.Kind{}, s.Datastorer.RollbackTx(ctx, tx, errs.E(errs.Database, err))
	}
	tk := org.Kind{
		ID:          testKindParams.OrgKindID,
		ExternalID:  testKindParams.OrgKindExtlID,
		Description: testKindParams.OrgKindDesc,
	}

	err = createStandardOrgKind(ctx, s.Datastorer, tx, pgAudit)
	if err != nil {
		return seedSet{}, org.Kind{}, s.Datastorer.RollbackTx(ctx, tx, errs.E(errs.Database, err))
	}

	sa := audit.SimpleAudit{
		First: pgAudit,
		Last:  pgAudit,
	}

	// write the Org to the database
	err = createOrgDB(ctx, s.Datastorer, tx, orgAudit{Org: o, SimpleAudit: sa})
	if err != nil {
		return seedSet{}, org.Kind{}, err
	}

	createAppParams := appstore.CreateAppParams{
		AppID:           a.ID,
		OrgID:           a.Org.ID,
		AppExtlID:       a.ExternalID.String(),
		AppName:         a.Name,
		AppDescription:  a.Description,
		CreateAppID:     pgAudit.App.ID,
		CreateUserID:    datastore.NewNullUUID(pgAudit.User.ID),
		CreateTimestamp: pgAudit.Moment,
		UpdateAppID:     pgAudit.App.ID,
		UpdateUserID:    datastore.NewNullUUID(pgAudit.User.ID),
		UpdateTimestamp: pgAudit.Moment,
	}

	// create app database record using appstore
	var rowsAffected int64
	rowsAffected, err = appstore.New(tx).CreateApp(ctx, createAppParams)
	if err != nil {
		return seedSet{}, org.Kind{}, s.Datastorer.RollbackTx(ctx, tx, errs.E(errs.Database, err))
	}

	if rowsAffected != 1 {
		return seedSet{}, org.Kind{}, s.Datastorer.RollbackTx(ctx, tx, errs.E(errs.Database, fmt.Sprintf("rows affected should be 1, actual: %d", rowsAffected)))
	}

	for _, key := range a.APIKeys {

		createAppAPIKeyParams := appstore.CreateAppAPIKeyParams{
			ApiKey:          key.Ciphertext(),
			AppID:           a.ID,
			DeactvDate:      key.DeactivationDate(),
			CreateAppID:     pgAudit.App.ID,
			CreateUserID:    datastore.NewNullUUID(pgAudit.User.ID),
			CreateTimestamp: pgAudit.Moment,
			UpdateAppID:     pgAudit.App.ID,
			UpdateUserID:    datastore.NewNullUUID(pgAudit.User.ID),
			UpdateTimestamp: pgAudit.Moment,
		}

		// create app API key database record using appstore
		var apiKeyRowsAffected int64
		apiKeyRowsAffected, err = appstore.New(tx).CreateAppAPIKey(ctx, createAppAPIKeyParams)
		if err != nil {
			return seedSet{}, org.Kind{}, s.Datastorer.RollbackTx(ctx, tx, errs.E(errs.Database, err))
		}

		if apiKeyRowsAffected != 1 {
			return seedSet{}, org.Kind{}, s.Datastorer.RollbackTx(ctx, tx, errs.E(errs.Database, fmt.Sprintf("rows affected should be 1, actual: %d", apiKeyRowsAffected)))
		}
	}

	// write Peter Gabriel to the database
	err = createUserDB(ctx, s.Datastorer, tx, pgUser, pgAudit)
	if err != nil {
		return seedSet{}, org.Kind{}, err
	}

	// write Phil Collins to the database
	err = createUserDB(ctx, s.Datastorer, tx, pcUser, pcAudit)
	if err != nil {
		return seedSet{}, org.Kind{}, err
	}

	return seedSet{org: o, app: a, user: pgUser, audit: sa}, tk, nil
}

func createPeterGabriel(o org.Org, a app.App) (user.User, audit.Audit) {
	// Peter Gabriel Person
	pgPrsn := person.Person{
		ID:  uuid.New(),
		Org: o,
	}

	// Peter Gabriel Person Profile
	pgPfl := person.Profile{ID: uuid.New(), Person: pgPrsn}
	pgPfl.FirstName = "Peter"
	pgPfl.LastName = "Gabriel"

	// Peter Gabriel User
	pgUser := user.User{
		ID:       uuid.New(),
		Username: strings.TrimSpace("pgabriel"),
		Org:      o,
		Profile:  pgPfl,
	}

	// Peter Gabriel Audit
	pgAudit := audit.Audit{
		App:    a,
		User:   pgUser,
		Moment: time.Now(),
	}

	return pgUser, pgAudit
}

func createPhilCollins(o org.Org, a app.App) (user.User, audit.Audit) {
	// Peter Gabriel Person
	pcPrsn := person.Person{
		ID:  uuid.New(),
		Org: o,
	}

	// Peter Gabriel Person Profile
	pgPfl := person.Profile{ID: uuid.New(), Person: pcPrsn}
	pgPfl.FirstName = "Phil"
	pgPfl.LastName = "Collins"

	// Peter Gabriel User
	pcUser := user.User{
		ID:       uuid.New(),
		Username: strings.TrimSpace("pcollins"),
		Org:      o,
		Profile:  pgPfl,
	}

	// Peter Gabriel Audit
	pcAudit := audit.Audit{
		App:    a,
		User:   pcUser,
		Moment: time.Now(),
	}

	return pcUser, pcAudit
}

func (s GenesisService) seedTest(ctx context.Context, tx pgx.Tx, k org.Kind) (seedSet, error) {
	var err error

	// create Org
	o := org.Org{
		ID:          uuid.New(),
		ExternalID:  secure.NewID(),
		Name:        "test",
		Description: "The test org is self explanatory",
		Kind:        k,
	}

	// initialize App and inject dependent fields
	a := app.App{
		ID:          uuid.New(),
		ExternalID:  secure.NewID(),
		Org:         o,
		Name:        "test",
		Description: "The test app is self explanatory",
		APIKeys:     nil,
	}

	keyDeactivation := time.Date(2099, 12, 31, 0, 0, 0, 0, time.UTC)
	err = a.AddNewKey(s.RandomStringGenerator, s.EncryptionKey, keyDeactivation)
	if err != nil {
		return seedSet{}, s.Datastorer.RollbackTx(ctx, tx, errs.E(errs.Internal, err))
	}

	// create Person
	prsn := person.Person{
		ID:  uuid.New(),
		Org: o,
	}

	// create Person Profile
	pfl := person.Profile{ID: uuid.New(), Person: prsn}
	pfl.FirstName = "Steve"
	pfl.LastName = "Hackett"

	// create User
	u := user.User{
		ID:       uuid.New(),
		Username: strings.TrimSpace("shackett"),
		Org:      o,
		Profile:  pfl,
	}

	//create Audit
	adt := audit.Audit{
		App:    a,
		User:   u,
		Moment: time.Now(),
	}

	sa := audit.SimpleAudit{
		First: adt,
		Last:  adt,
	}

	// write the Org to the database
	err = createOrgDB(ctx, s.Datastorer, tx, orgAudit{Org: o, SimpleAudit: sa})
	if err != nil {
		return seedSet{}, err
	}

	createAppParams := appstore.CreateAppParams{
		AppID:           a.ID,
		OrgID:           a.Org.ID,
		AppExtlID:       a.ExternalID.String(),
		AppName:         a.Name,
		AppDescription:  a.Description,
		CreateAppID:     adt.App.ID,
		CreateUserID:    datastore.NewNullUUID(adt.User.ID),
		CreateTimestamp: adt.Moment,
		UpdateAppID:     adt.App.ID,
		UpdateUserID:    datastore.NewNullUUID(adt.User.ID),
		UpdateTimestamp: adt.Moment,
	}

	// create app database record using appstore
	var rowsAffected int64
	rowsAffected, err = appstore.New(tx).CreateApp(ctx, createAppParams)
	if err != nil {
		return seedSet{}, s.Datastorer.RollbackTx(ctx, tx, errs.E(errs.Database, err))
	}

	if rowsAffected != 1 {
		return seedSet{}, s.Datastorer.RollbackTx(ctx, tx, errs.E(errs.Database, fmt.Sprintf("rows affected should be 1, actual: %d", rowsAffected)))
	}

	for _, key := range a.APIKeys {

		createAppAPIKeyParams := appstore.CreateAppAPIKeyParams{
			ApiKey:          key.Ciphertext(),
			AppID:           a.ID,
			DeactvDate:      key.DeactivationDate(),
			CreateAppID:     adt.App.ID,
			CreateUserID:    datastore.NewNullUUID(adt.User.ID),
			CreateTimestamp: adt.Moment,
			UpdateAppID:     adt.App.ID,
			UpdateUserID:    datastore.NewNullUUID(adt.User.ID),
			UpdateTimestamp: adt.Moment,
		}

		// create app API key database record using appstore
		var apiKeyRowsAffected int64
		apiKeyRowsAffected, err = appstore.New(tx).CreateAppAPIKey(ctx, createAppAPIKeyParams)
		if err != nil {
			return seedSet{}, s.Datastorer.RollbackTx(ctx, tx, errs.E(errs.Database, err))
		}

		if apiKeyRowsAffected != 1 {
			return seedSet{}, s.Datastorer.RollbackTx(ctx, tx, errs.E(errs.Database, fmt.Sprintf("rows affected should be 1, actual: %d", apiKeyRowsAffected)))
		}
	}

	// write the User to the database
	err = createUserDB(ctx, s.Datastorer, tx, u, adt)
	if err != nil {
		return seedSet{}, err
	}

	return seedSet{org: o, app: a, user: u, audit: sa}, nil
}

func genesisHasOccurred(ctx context.Context, dbtx orgstore.DBTX) (err error) {
	var (
		existingOrgs         []orgstore.FindOrgsByKindExtlIDRow
		hasGenesisOrgTypeRow = true
		hasGenesisOrgRow     = true
	)

	// validate Genesis records do not exist already
	// first: check org_type
	_, err = orgstore.New(dbtx).FindOrgKindByExtlID(ctx, genesisOrgTypeString)
	if err != nil {
		if err != pgx.ErrNoRows {
			return errs.E(errs.Database, err)
		}
		hasGenesisOrgTypeRow = false
	}

	// last: check org
	existingOrgs, err = orgstore.New(dbtx).FindOrgsByKindExtlID(ctx, genesisOrgTypeString)
	if err != nil {
		return errs.E(errs.Database, err)
	}
	if len(existingOrgs) == 0 {
		hasGenesisOrgRow = false
	}

	if hasGenesisOrgTypeRow || hasGenesisOrgRow {
		return errs.E(errs.Validation, "No prior data should exist when executing Genesis Service")
	}

	return nil
}
<|endoftext|>


<commit_before>package service

import (
	"github.com/SudoQ/ganache/item"
	"github.com/gorilla/mux"
	"encoding/json"
	"io/ioutil"
	"log"
	"time"
	"net/http"
	"fmt"
)

type Service struct {
	URL    string `json:"url"`
	Port   string `json:"port"`
	Period time.Duration `json:"-"`
	Limit  int `json:"limit"`
	Cache  *item.Item `json:"-"`
}

func New(url, port string, limit int) *Service {
	period := LimitToDuration(limit)
	return &Service{
		URL:    url,
		Port:   port,
		Period: period,
		Limit:  limit,
		Cache:  nil,
	}
}

func LimitToDuration(limit int) time.Duration {
	return time.Duration(time.Duration(limit/60) * time.Minute)
}

func NewFromJSON(jsonBlob []byte) (*Service, error) {
	var srv Service
	err := json.Unmarshal(jsonBlob, &srv)
	if err != nil{
		return nil, err
	}
	srv.Period = LimitToDuration(srv.Limit)
	return &srv, nil
}

func NewFromFile(filename string) (*Service, error) {
	jsonBlob, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	return NewFromJSON(jsonBlob)
}

func (this *Service) LoadConfig(filename string) error {
	jsonBlob, err := ioutil.ReadFile(filename)
	if err != nil {
		return err
	}
	err = json.Unmarshal(jsonBlob, this)
	return err
}

func (this *Service) JSON() ([]byte, error) {
	// Return service configuration as JSON
	blob, err := json.Marshal(this)
	if err != nil {
		return nil, err
	}
	return blob, nil
}

func (this *Service) Chaos(err error) bool {
	if err != nil {
		// TODO Log error
		log.Println(err)
		return true
	}
	return false
}

func (this *Service) SaveConfig(filename string) error {
	jsonBlob, err := this.JSON()
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(filename, jsonBlob, 0644)
	return err
}

func (this *Service) Collect() {
	for {
		now := time.Now()
		timeDelta := now.Sub(this.Cache.Timestamp)
		if this.Cache.Timestamp.Before(now) && timeDelta > this.Period {
			log.Printf("GET %s\n", this.URL)

			resp, err := http.Get(this.URL)
			if this.Chaos(err) {
				// TODO handle error
				log.Fatal(err)
			}

			payload, err := ioutil.ReadAll(resp.Body)
			if this.Chaos(err) {
				// TODO handle error
				log.Fatal(err)
			}

			// TODO Store header info?
			newItem := item.New(now, payload)
			this.Cache = newItem
			cacheFilename := "cache.json"
			err = this.Cache.WriteFile(cacheFilename)
			if this.Chaos(err){
				log.Fatal(err)
			}
			log.Printf("Saved cache to %s\n", cacheFilename)

		}
		select {
		case <-time.After(this.Period):
			continue
		}
	}
}

func (this *Service) LoadCache(filename string) error {
	content, err := ioutil.ReadFile(filename)
	if err != nil{
		return err
	}
	newItem, err := item.NewFromJSON(content)
	if err != nil {
		return err
	}
	this.Cache = newItem
	return nil
}

func (this * Service) Info() string {
	return fmt.Sprintf("Ganache API caching service v0.1")
}

func (this *Service) Run() {
	err := this.LoadCache("cache.json")
	if err != nil {
		log.Fatal(err)
	}
	go this.Collect()
	r := mux.NewRouter()
	r.HandleFunc("/", this.HomeHandler)
	r.HandleFunc("/info", this.InfoHandler)
	r.HandleFunc("/cache.json", this.CacheHandler)
	port := fmt.Sprintf(":%s", this.Port)
	http.ListenAndServe(port, r)
}

func (this *Service) HomeHandler(w http.ResponseWriter, r *http.Request) {
	w.Write(this.Cache.Payload)
}

func (this *Service) InfoHandler(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte(this.Info()))
}

func (this *Service) CacheHandler(w http.ResponseWriter, r *http.Request) {
	response, err := this.Cache.JSON()
	if err != nil {
		// TODO
		log.Fatal(err)
	}
	w.Write(response)
}
<commit_msg>Fixed bug with convertions from limit to period<commit_after>package service

import (
	"github.com/SudoQ/ganache/item"
	"github.com/gorilla/mux"
	"encoding/json"
	"io/ioutil"
	"log"
	"time"
	"net/http"
	"fmt"
	"errors"
)

type Service struct {
	URL    string `json:"url"`
	Port   string `json:"port"`
	Period time.Duration `json:"-"`
	Limit  uint `json:"limit"`
	Cache  *item.Item `json:"-"`
}

func New(url, port string, limit uint) *Service {
	period, err := LimitToDuration(limit)
	if err != nil {
		limit = 1
		period, _ = LimitToDuration(limit)
	}
	return &Service{
		URL:    url,
		Port:   port,
		Period: period,
		Limit:  limit,
		Cache:  nil,
	}
}

func LimitToDuration(limit uint) (time.Duration, error) {
	if limit == 0 {
		return 0, errors.New("Division with zero")
	}
	return time.Duration(time.Duration(60/limit) * time.Minute), nil
}

func NewFromJSON(jsonBlob []byte) (*Service, error) {
	var srv Service
	err := json.Unmarshal(jsonBlob, &srv)
	if err != nil{
		return nil, err
	}
	srv.Period, err = LimitToDuration(srv.Limit)
	if err != nil {
		srv.Limit = 1
		srv.Period, _ = LimitToDuration(srv.Limit)
	}
	return &srv, nil
}

func NewFromFile(filename string) (*Service, error) {
	jsonBlob, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	return NewFromJSON(jsonBlob)
}

func (this *Service) LoadConfig(filename string) error {
	jsonBlob, err := ioutil.ReadFile(filename)
	if err != nil {
		return err
	}
	err = json.Unmarshal(jsonBlob, this)
	return err
}

func (this *Service) JSON() ([]byte, error) {
	// Return service configuration as JSON
	blob, err := json.Marshal(this)
	if err != nil {
		return nil, err
	}
	return blob, nil
}

func (this *Service) Chaos(err error) bool {
	if err != nil {
		// TODO Log error
		log.Println(err)
		return true
	}
	return false
}

func (this *Service) SaveConfig(filename string) error {
	jsonBlob, err := this.JSON()
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(filename, jsonBlob, 0644)
	return err
}

func (this *Service) Collect() {
	for {
		now := time.Now()
		timeDelta := now.Sub(this.Cache.Timestamp)
		if this.Cache.Timestamp.Before(now) && timeDelta > this.Period {
			log.Printf("GET %s\n", this.URL)

			resp, err := http.Get(this.URL)
			if this.Chaos(err) {
				// TODO handle error
				log.Fatal(err)
			}

			payload, err := ioutil.ReadAll(resp.Body)
			if this.Chaos(err) {
				// TODO handle error
				log.Fatal(err)
			}

			// TODO Store header info?
			newItem := item.New(now, payload)
			this.Cache = newItem
			cacheFilename := "cache.json"
			err = this.Cache.WriteFile(cacheFilename)
			if this.Chaos(err){
				log.Fatal(err)
			}
			log.Printf("Saved cache to %s\n", cacheFilename)

		}
		select {
		case <-time.After(this.Period):
			continue
		}
	}
}

func (this *Service) LoadCache(filename string) error {
	content, err := ioutil.ReadFile(filename)
	if err != nil{
		return err
	}
	newItem, err := item.NewFromJSON(content)
	if err != nil {
		return err
	}
	this.Cache = newItem
	return nil
}

func (this * Service) Info() string {
	return fmt.Sprintf("Ganache API caching service v0.1")
}

func (this *Service) Run() {
	err := this.LoadCache("cache.json")
	if err != nil {
		log.Fatal(err)
	}
	go this.Collect()
	r := mux.NewRouter()
	r.HandleFunc("/", this.HomeHandler)
	r.HandleFunc("/info", this.InfoHandler)
	r.HandleFunc("/cache.json", this.CacheHandler)
	port := fmt.Sprintf(":%s", this.Port)
	http.ListenAndServe(port, r)
}

func (this *Service) HomeHandler(w http.ResponseWriter, r *http.Request) {
	w.Write(this.Cache.Payload)
}

func (this *Service) InfoHandler(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte(this.Info()))
}

func (this *Service) CacheHandler(w http.ResponseWriter, r *http.Request) {
	response, err := this.Cache.JSON()
	if err != nil {
		// TODO
		log.Fatal(err)
	}
	w.Write(response)
}
<|endoftext|>


<commit_before>package service

import (
	"fmt"
	"github.com/ninjasphere/app-presets/model"
)

type PresetsService struct {
	Model *model.Presets
	Save  func(*model.Presets)
	Conn        *ninja.Connection
	Log         *logger.Logger
	initialized bool
}

func (ps *PresetsService) Init() error {
	if ps.Log == nil {
		return fmt.Errorf("illegal state: no logger")
	}
	if ps.Model == nil {
		return fmt.Errorf("illegal state: Model is nil")
	}
	if ps.Save == nil {
		return fmt.Errorf("illegal state: Save is nil")
	}
	if ps.Conn == nil {
		return fmt.Errorf("illegal state: Conn is nil")
	}
	ps.initialized = true
	return nil
}

func (ps *PresetsService) Destroy() error {
	ps.initialized = false
	return nil
}

func (ps *PresetsService) checkInit() {
	if ps.Log == nil {
		ps.Log = logger.GetLogger("com.ninja.app-presets")
	}
	if !ps.initialized {
		ps.Log.Fatalf("illegal state: the service is not initialized")
	}
}

// see: http://schema.ninjablocks.com/service/presets#listPresetable
func (ps *PresetsService) ListPresetable(scope string) ([]model.ThingState, error) {
	return make([]model.ThingState, 0, 0), fmt.Errorf("unimplemented function: ListPresetable")
}

// see: http://schema.ninjablocks.com/service/presets#fetchScenes
func (ps *PresetsService) FetchScenes(scope string) ([]model.Scene, error) {
	return make([]model.Scene, 0, 0), fmt.Errorf("unimplemented function: FetchScenes")
}

// see: http://schema.ninjablocks.com/service/presets#fetchScene
func (ps *PresetsService) FetchScene(id string) (*model.Scene, error) {
	return nil, fmt.Errorf("unimplemented function: FetchScene")
}

// see: http://schema.ninjablocks.com/service/presets#storeScene
func (ps *PresetsService) StoreScene(model *model.Scene) error {
	return fmt.Errorf("unimplemented function: StoreScene")
}

// see: http://schema.ninjablocks.com/service/presets#applyScene
func (ps *PresetsService) ApplyScene(id string) error {
	return fmt.Errorf("unimplemented function: ApplyScene")
}
<commit_msg>whitespace formatting.<commit_after>package service

import (
	"fmt"
	"github.com/ninjasphere/app-presets/model"
)

type PresetsService struct {
	Model       *model.Presets
	Save        func(*model.Presets)
	Conn        *ninja.Connection
	Log         *logger.Logger
	initialized bool
}

func (ps *PresetsService) Init() error {
	if ps.Log == nil {
		return fmt.Errorf("illegal state: no logger")
	}
	if ps.Model == nil {
		return fmt.Errorf("illegal state: Model is nil")
	}
	if ps.Save == nil {
		return fmt.Errorf("illegal state: Save is nil")
	}
	if ps.Conn == nil {
		return fmt.Errorf("illegal state: Conn is nil")
	}
	ps.initialized = true
	return nil
}

func (ps *PresetsService) Destroy() error {
	ps.initialized = false
	return nil
}

func (ps *PresetsService) checkInit() {
	if ps.Log == nil {
		ps.Log = logger.GetLogger("com.ninja.app-presets")
	}
	if !ps.initialized {
		ps.Log.Fatalf("illegal state: the service is not initialized")
	}
}

// see: http://schema.ninjablocks.com/service/presets#listPresetable
func (ps *PresetsService) ListPresetable(scope string) ([]model.ThingState, error) {
	return make([]model.ThingState, 0, 0), fmt.Errorf("unimplemented function: ListPresetable")
}

// see: http://schema.ninjablocks.com/service/presets#fetchScenes
func (ps *PresetsService) FetchScenes(scope string) ([]model.Scene, error) {
	return make([]model.Scene, 0, 0), fmt.Errorf("unimplemented function: FetchScenes")
}

// see: http://schema.ninjablocks.com/service/presets#fetchScene
func (ps *PresetsService) FetchScene(id string) (*model.Scene, error) {
	return nil, fmt.Errorf("unimplemented function: FetchScene")
}

// see: http://schema.ninjablocks.com/service/presets#storeScene
func (ps *PresetsService) StoreScene(model *model.Scene) error {
	return fmt.Errorf("unimplemented function: StoreScene")
}

// see: http://schema.ninjablocks.com/service/presets#applyScene
func (ps *PresetsService) ApplyScene(id string) error {
	return fmt.Errorf("unimplemented function: ApplyScene")
}
<|endoftext|>