<commit_before>package master

import (
	"github.com/KIT-MAMID/mamid/model"
	"github.com/KIT-MAMID/mamid/msp"
	"github.com/stretchr/testify/assert"
	"sync"
	"testing"
)

func createDB(t *testing.T) (db *model.DB, err error) {
	// Setup database
	db, path, err := model.InitializeTestDB()
	t.Logf("creating test db: %s", path)

	tx := db.Begin()

	dbSlave := model.Slave{
		Hostname:             "host1",
		Port:                 1,
		MongodPortRangeBegin: 2,
		MongodPortRangeEnd:   3,
		PersistentStorage:    true,
		Mongods:              []*model.Mongod{},
		ConfiguredState:      model.SlaveStateActive,
	}
	assert.NoError(t, tx.Create(&dbSlave).Error)
	dbReplSet := model.ReplicaSet{
		Name: "foo",
	}
	assert.NoError(t, tx.Create(&dbReplSet).Error)
	m1 := model.Mongod{
		Port:          2000,
		ReplSetName:   "repl1",
		ParentSlaveID: dbSlave.ID,
		ReplicaSetID:  model.NullIntValue(dbReplSet.ID),
	}
	assert.NoError(t, tx.Create(&m1).Error)
	des1 := model.MongodState{
		ParentMongodID:         m1.ID,
		IsShardingConfigServer: false,
		ExecutionState:         model.MongodExecutionStateRunning,
	}
	assert.NoError(t, tx.Create(&des1).Error)
	assert.NoError(t, tx.Model(&m1).Update("DesiredStateID", des1.ID).Error)

	tx.Commit()
	return
}

type FakeMSPClient struct {
	msp.MSPClient
	Status []msp.Mongod
	Error  *msp.Error
}

func (m FakeMSPClient) RequestStatus(Target msp.HostPort) ([]msp.Mongod, *msp.Error) {
	return m.Status, m.Error
}

func TestMonitor_observeSlave(t *testing.T) {
	db, err := createDB(t)
	defer db.CloseAndDrop()
	assert.NoError(t, err)

	wg := new(sync.WaitGroup)
	bus := NewBus()
	readChannel := bus.GetNewReadChannel()
	monitor := Monitor{
		DB:              db,
		BusWriteChannel: bus.GetNewWriteChannel(),
	}

	wg.Add(1)
	go func() {
		bus.Run()
		wg.Done()
	}()

	//Observe Slave
	var slave model.Slave
	{
		tx := db.Begin()
		assert.NoError(t, tx.First(&slave).Error)
		tx.Rollback()
	}

	monitor.handleObservation([]msp.Mongod{
		msp.Mongod{
			Port:                    2000,
			ReplicaSetName:          "repl1",
			ReplicaSetMembers:       []msp.HostPort{},
			ShardingConfigServer:    false,
			StatusError:             nil,
			LastEstablishStateError: nil,
			State: msp.MongodStateRunning,
		},
	}, nil, slave)

	var mongod model.Mongod
	{
		tx := db.Begin()
		assert.NoError(t, tx.First(&mongod).Error)
		assert.Nil(t, tx.Model(&mongod).Related(&mongod.ObservedState, "ObservedState").Error, "after observation, the observed state should be != nil")
		tx.Rollback()
	}
	assert.Equal(t, model.MongodExecutionStateRunning, mongod.ObservedState.ExecutionState)

	connStatusX := <-readChannel
	connStatus, ok := connStatusX.(model.ConnectionStatus)
	assert.True(t, ok)
	assert.False(t, connStatus.Unreachable)

	mismatchX := <-readChannel
	mismatch, ok := mismatchX.(model.MongodMatchStatus)
	assert.False(t, mismatch.Mismatch)

	//-----------------
	//Slave cannot observe mongod
	//-----------------
	{
		tx := db.Begin()
		assert.NoError(t, tx.First(&slave).Error)
		tx.Rollback()
	}

	monitor.handleObservation([]msp.Mongod{
		msp.Mongod{
			Port:           2000,
			ReplicaSetName: "repl1",
			StatusError: &msp.Error{
				Identifier:  "foo",
				Description: "cannot observe mongod",
			},
		},
	}, nil, slave)

	{
		tx := db.Begin()
		assert.NoError(t, tx.First(&mongod).Error)

		//Mongod should have an observation error
		tx.Model(&mongod).Related(&mongod.ObservationError, "ObservationError")
		assert.EqualValues(t, "cannot observe mongod", mongod.ObservationError.Description)
		tx.Rollback()
	}
	assert.NotZero(t, mongod.ObservationErrorID)

	connStatusX = <-readChannel
	connStatus, ok = connStatusX.(model.ConnectionStatus)
	assert.True(t, ok)
	assert.False(t, connStatus.Unreachable)

	<-readChannel //mismatch

	//-----------------
	//Mongod gone
	//-----------------
	monitor.MSPClient = FakeMSPClient{
		Status: []msp.Mongod{},
		Error:  nil,
	}

	{
		tx := db.Begin()
		assert.NoError(t, tx.First(&slave).Error)
		tx.Rollback()
	}

	monitor.handleObservation([]msp.Mongod{}, nil, slave)

	{
		tx := db.Begin()
		assert.NoError(t, tx.First(&mongod).Error)

		//Mongod should not have observed state anymore
		assert.True(t, tx.Model(&mongod).Related(&mongod.ObservedState, "ObservedState").RecordNotFound())
		tx.Rollback()
	}

	connStatusX = <-readChannel
	connStatus, ok = connStatusX.(model.ConnectionStatus)
	assert.True(t, ok)
	assert.False(t, connStatus.Unreachable)

	<-readChannel //mismatch

	//-----------------
	//Slave becomes unreachable
	//-----------------
	monitor.MSPClient = FakeMSPClient{
		Status: []msp.Mongod{},
		Error:  &msp.Error{Identifier: msp.CommunicationError},
	}

	{
		tx := db.Begin()
		assert.NoError(t, tx.First(&slave).Error)
		tx.Rollback()
	}

	monitor.handleObservation([]msp.Mongod{}, &msp.Error{Identifier: msp.CommunicationError}, slave)

	connStatusX = <-readChannel
	connStatus, ok = connStatusX.(model.ConnectionStatus)
	assert.True(t, ok)
	assert.True(t, connStatus.Unreachable)

	bus.Kill()
	wg.Wait()
}

func TestMonitor_compareStates(t *testing.T) {

	db, err := createDB(t)
	assert.NoError(t, err)
	defer db.CloseAndDrop()

	monitor := Monitor{
		DB: db,
	}

	// Test without observed state
	tx := db.Begin()

	var dbMongod model.Mongod
	assert.NoError(t, tx.First(&dbMongod).Error)

	msg, err := monitor.compareStates(tx, dbMongod)
	assert.NoError(t, err)
	assert.EqualValues(t, true, msg.Mismatch, "Mongods without ObservedState should always result in mismatch")

	tx.Rollback()
	tx = db.Begin()

	// Test with equal observed state
	// => duplicate DesiredState
	assert.NoError(t, tx.Model(&dbMongod).Related(&dbMongod.ObservedState, "DesiredState").Error)
	dbMongod.ObservedState.ID = 0
	assert.NoError(t, tx.Create(&dbMongod.ObservedState).Error)
	assert.NoError(t, tx.Model(&dbMongod).Update("ObservedStateID", dbMongod.ObservedState.ID).Error)

	msg, err = monitor.compareStates(tx, dbMongod)
	assert.NoError(t, err)
	assert.EqualValues(t, false, msg.Mismatch, "Mongods with equal Observed & Desired states should not result in a mismatch")

	// Save this state, we check single unequal attributes from here on
	tx.Commit()
	tx = db.Begin()

	// Test with unequal execution state
	assert.NoError(t, tx.Model(&dbMongod.ObservedState).Update("ExecutionState", model.MongodExecutionStateNotRunning).Error)
	msg, err = monitor.compareStates(tx, dbMongod)
	assert.NoError(t, err)
	assert.EqualValues(t, true, msg.Mismatch, "unequal ExecutionState should result in a mismatch")

	tx.Rollback()
	tx = db.Begin()

	// Test with unequal IsShardingConfigServer field
	assert.NoError(t, tx.Model(&dbMongod.ObservedState).Update("IsShardingConfigServer", !dbMongod.DesiredState.IsShardingConfigServer).Error)
	msg, err = monitor.compareStates(tx, dbMongod)
	assert.NoError(t, err)
	assert.EqualValues(t, true, msg.Mismatch, "unequal IsShardingConfigServer should result in a mismatch")

	tx.Rollback()
	tx = db.Begin()

	var slave model.Slave
	assert.NoError(t, tx.First(&slave).Error)

	desiredMember1 := model.ReplicaSetMember{
		MongodStateID: dbMongod.DesiredStateID,
		Hostname:      slave.Hostname,
		Port:          dbMongod.Port,
	}
	assert.NoError(t, tx.Create(&desiredMember1).Error)
	assert.True(t, dbMongod.ObservedStateID.Valid)
	observedMember1 := model.ReplicaSetMember{
		MongodStateID: dbMongod.ObservedStateID.Int64,
		Hostname:      desiredMember1.Hostname,
		Port:          desiredMember1.Port,
	}
	assert.NoError(t, tx.Create(&observedMember1).Error)

	// Test with equal ReplicaSetMembers
	msg, err = monitor.compareStates(tx, dbMongod)
	assert.NoError(t, err)
	assert.EqualValues(t, false, msg.Mismatch, "equal ReplicaSetMembers should not result in a mismatch")

	// Keep them equal
	tx.Commit()
	tx = db.Begin()

	// Test with same number of members but different values
	assert.NoError(t, tx.Model(&observedMember1).Update("Port", observedMember1.Port+1).Error)
	assert.NoError(t, tx.Model(&observedMember1).Update("Hostname", "someunknownhost").Error)

	var desiredMemberRefetched model.ReplicaSetMember
	assert.NoError(t, tx.Model(&dbMongod).Related(&desiredMemberRefetched, "DesiredState").Error)
	assert.EqualValues(t, slave.Hostname, desiredMemberRefetched.Hostname)
	assert.EqualValues(t, "someunknownhost", observedMember1.Hostname)

	msg, err = monitor.compareStates(tx, dbMongod)
	assert.NoError(t, err)
	assert.EqualValues(t, true, msg.Mismatch, "unequal ReplicaSetMembers should result in a mismatch")

	tx.Rollback()
	tx = db.Begin()

	// Test with different number of members but same values
	observedMember2 := observedMember1
	observedMember2.Hostname = "anotherhost"
	assert.False(t, observedMember2.Hostname == observedMember1.Hostname, "error in test logic")
	observedMember2.ID = 0
	assert.NoError(t, tx.Create(&observedMember2).Error)

	msg, err = monitor.compareStates(tx, dbMongod)
	assert.NoError(t, err)
	assert.EqualValues(t, true, msg.Mismatch, "different sets of ReplicaSetMembers should result in a mismatch")

	tx.Rollback()

}

func TestMonitor_ReplicaSetMembersEquivalent(t *testing.T) {
	assert.True(t, ReplicaSetMembersEquivalent(model.ReplicaSetMember{Hostname: "host1", Port: 100}, model.ReplicaSetMember{Hostname: "host1", Port: 100}))
	assert.False(t, ReplicaSetMembersEquivalent(model.ReplicaSetMember{Hostname: "host1", Port: 100}, model.ReplicaSetMember{Hostname: "host1", Port: 200}))
	assert.False(t, ReplicaSetMembersEquivalent(model.ReplicaSetMember{Hostname: "host1", Port: 100}, model.ReplicaSetMember{Hostname: "host2", Port: 100}))
	assert.False(t, ReplicaSetMembersEquivalent(model.ReplicaSetMember{Hostname: "host1", Port: 100}, model.ReplicaSetMember{Hostname: "host2", Port: 200}))
}
<commit_msg>FIX: monitor: adjust test to model and msp changes<commit_after>package master

import (
	"github.com/KIT-MAMID/mamid/model"
	"github.com/KIT-MAMID/mamid/msp"
	"github.com/stretchr/testify/assert"
	"sync"
	"testing"
)

func createDB(t *testing.T) (db *model.DB, err error) {
	// Setup database
	db, path, err := model.InitializeTestDB()
	t.Logf("creating test db: %s", path)

	tx := db.Begin()

	dbSlave := model.Slave{
		Hostname:             "host1",
		Port:                 1,
		MongodPortRangeBegin: 2,
		MongodPortRangeEnd:   3,
		PersistentStorage:    true,
		Mongods:              []*model.Mongod{},
		ConfiguredState:      model.SlaveStateActive,
	}
	assert.NoError(t, tx.Create(&dbSlave).Error)
	dbReplSet := model.ReplicaSet{
		Name: "foo",
	}
	assert.NoError(t, tx.Create(&dbReplSet).Error)
	m1 := model.Mongod{
		Port:          2000,
		ReplSetName:   "repl1",
		ParentSlaveID: dbSlave.ID,
		ReplicaSetID:  model.NullIntValue(dbReplSet.ID),
	}
	assert.NoError(t, tx.Create(&m1).Error)
	des1 := model.MongodState{
		ParentMongodID: m1.ID,
		ShardingRole:   model.ShardingRoleNone,
		ExecutionState: model.MongodExecutionStateRunning,
	}
	assert.NoError(t, tx.Create(&des1).Error)
	assert.NoError(t, tx.Model(&m1).Update("DesiredStateID", des1.ID).Error)

	tx.Commit()
	return
}

type FakeMSPClient struct {
	msp.MSPClient
	Status []msp.Mongod
	Error  *msp.Error
}

func (m FakeMSPClient) RequestStatus(Target msp.HostPort) ([]msp.Mongod, *msp.Error) {
	return m.Status, m.Error
}

func TestMonitor_observeSlave(t *testing.T) {
	db, err := createDB(t)
	defer db.CloseAndDrop()
	assert.NoError(t, err)

	wg := new(sync.WaitGroup)
	bus := NewBus()
	readChannel := bus.GetNewReadChannel()
	monitor := Monitor{
		DB:              db,
		BusWriteChannel: bus.GetNewWriteChannel(),
	}

	wg.Add(1)
	go func() {
		bus.Run()
		wg.Done()
	}()

	//Observe Slave
	var slave model.Slave
	{
		tx := db.Begin()
		assert.NoError(t, tx.First(&slave).Error)
		tx.Rollback()
	}

	monitor.handleObservation([]msp.Mongod{
		msp.Mongod{
			Port: 2000,
			ReplicaSetConfig: msp.ReplicaSetConfig{
				ReplicaSetName:    "repl1",
				ReplicaSetMembers: []msp.HostPort{},
				ShardingRole:      msp.ShardingRoleNone,
			},
			StatusError:             nil,
			LastEstablishStateError: nil,
			State: msp.MongodStateRunning,
		},
	}, nil, slave)

	var mongod model.Mongod
	{
		tx := db.Begin()
		assert.NoError(t, tx.First(&mongod).Error)
		assert.Nil(t, tx.Model(&mongod).Related(&mongod.ObservedState, "ObservedState").Error, "after observation, the observed state should be != nil")
		tx.Rollback()
	}
	assert.Equal(t, model.MongodExecutionStateRunning, mongod.ObservedState.ExecutionState)

	connStatusX := <-readChannel
	connStatus, ok := connStatusX.(model.ConnectionStatus)
	assert.True(t, ok)
	assert.False(t, connStatus.Unreachable)

	mismatchX := <-readChannel
	mismatch, ok := mismatchX.(model.MongodMatchStatus)
	assert.False(t, mismatch.Mismatch)

	//-----------------
	//Slave cannot observe mongod
	//-----------------
	{
		tx := db.Begin()
		assert.NoError(t, tx.First(&slave).Error)
		tx.Rollback()
	}

	monitor.handleObservation([]msp.Mongod{
		msp.Mongod{
			Port: 2000,
			ReplicaSetConfig: msp.ReplicaSetConfig{
				ReplicaSetName: "repl1",
			},
			StatusError: &msp.Error{
				Identifier:  "foo",
				Description: "cannot observe mongod",
			},
		},
	}, nil, slave)

	{
		tx := db.Begin()
		assert.NoError(t, tx.First(&mongod).Error)

		//Mongod should have an observation error
		tx.Model(&mongod).Related(&mongod.ObservationError, "ObservationError")
		assert.EqualValues(t, "cannot observe mongod", mongod.ObservationError.Description)
		tx.Rollback()
	}
	assert.NotZero(t, mongod.ObservationErrorID)

	connStatusX = <-readChannel
	connStatus, ok = connStatusX.(model.ConnectionStatus)
	assert.True(t, ok)
	assert.False(t, connStatus.Unreachable)

	<-readChannel //mismatch

	//-----------------
	//Mongod gone
	//-----------------
	monitor.MSPClient = FakeMSPClient{
		Status: []msp.Mongod{},
		Error:  nil,
	}

	{
		tx := db.Begin()
		assert.NoError(t, tx.First(&slave).Error)
		tx.Rollback()
	}

	monitor.handleObservation([]msp.Mongod{}, nil, slave)

	{
		tx := db.Begin()
		assert.NoError(t, tx.First(&mongod).Error)

		//Mongod should not have observed state anymore
		assert.True(t, tx.Model(&mongod).Related(&mongod.ObservedState, "ObservedState").RecordNotFound())
		tx.Rollback()
	}

	connStatusX = <-readChannel
	connStatus, ok = connStatusX.(model.ConnectionStatus)
	assert.True(t, ok)
	assert.False(t, connStatus.Unreachable)

	<-readChannel //mismatch

	//-----------------
	//Slave becomes unreachable
	//-----------------
	monitor.MSPClient = FakeMSPClient{
		Status: []msp.Mongod{},
		Error:  &msp.Error{Identifier: msp.CommunicationError},
	}

	{
		tx := db.Begin()
		assert.NoError(t, tx.First(&slave).Error)
		tx.Rollback()
	}

	monitor.handleObservation([]msp.Mongod{}, &msp.Error{Identifier: msp.CommunicationError}, slave)

	connStatusX = <-readChannel
	connStatus, ok = connStatusX.(model.ConnectionStatus)
	assert.True(t, ok)
	assert.True(t, connStatus.Unreachable)

	bus.Kill()
	wg.Wait()
}

func TestMonitor_compareStates(t *testing.T) {

	db, err := createDB(t)
	assert.NoError(t, err)
	defer db.CloseAndDrop()

	monitor := Monitor{
		DB: db,
	}

	// Test without observed state
	tx := db.Begin()

	var dbMongod model.Mongod
	assert.NoError(t, tx.First(&dbMongod).Error)

	msg, err := monitor.compareStates(tx, dbMongod)
	assert.NoError(t, err)
	assert.EqualValues(t, true, msg.Mismatch, "Mongods without ObservedState should always result in mismatch")

	tx.Rollback()
	tx = db.Begin()

	// Test with equal observed state
	// => duplicate DesiredState
	assert.NoError(t, tx.Model(&dbMongod).Related(&dbMongod.ObservedState, "DesiredState").Error)
	dbMongod.ObservedState.ID = 0
	assert.NoError(t, tx.Create(&dbMongod.ObservedState).Error)
	assert.NoError(t, tx.Model(&dbMongod).Update("ObservedStateID", dbMongod.ObservedState.ID).Error)

	msg, err = monitor.compareStates(tx, dbMongod)
	assert.NoError(t, err)
	assert.EqualValues(t, false, msg.Mismatch, "Mongods with equal Observed & Desired states should not result in a mismatch")

	// Save this state, we check single unequal attributes from here on
	tx.Commit()
	tx = db.Begin()

	// Test with unequal execution state
	assert.NoError(t, tx.Model(&dbMongod.ObservedState).Update("ExecutionState", model.MongodExecutionStateNotRunning).Error)
	msg, err = monitor.compareStates(tx, dbMongod)
	assert.NoError(t, err)
	assert.EqualValues(t, true, msg.Mismatch, "unequal ExecutionState should result in a mismatch")

	tx.Rollback()
	tx = db.Begin()

	// Test with unequal IsShardingConfigServer field
	assert.NoError(t, tx.Model(&dbMongod.ObservedState).Update("IsShardingConfigServer", !dbMongod.DesiredState.IsShardingConfigServer).Error)
	msg, err = monitor.compareStates(tx, dbMongod)
	assert.NoError(t, err)
	assert.EqualValues(t, true, msg.Mismatch, "unequal IsShardingConfigServer should result in a mismatch")

	tx.Rollback()
	tx = db.Begin()

	var slave model.Slave
	assert.NoError(t, tx.First(&slave).Error)

	desiredMember1 := model.ReplicaSetMember{
		MongodStateID: dbMongod.DesiredStateID,
		Hostname:      slave.Hostname,
		Port:          dbMongod.Port,
	}
	assert.NoError(t, tx.Create(&desiredMember1).Error)
	assert.True(t, dbMongod.ObservedStateID.Valid)
	observedMember1 := model.ReplicaSetMember{
		MongodStateID: dbMongod.ObservedStateID.Int64,
		Hostname:      desiredMember1.Hostname,
		Port:          desiredMember1.Port,
	}
	assert.NoError(t, tx.Create(&observedMember1).Error)

	// Test with equal ReplicaSetMembers
	msg, err = monitor.compareStates(tx, dbMongod)
	assert.NoError(t, err)
	assert.EqualValues(t, false, msg.Mismatch, "equal ReplicaSetMembers should not result in a mismatch")

	// Keep them equal
	tx.Commit()
	tx = db.Begin()

	// Test with same number of members but different values
	assert.NoError(t, tx.Model(&observedMember1).Update("Port", observedMember1.Port+1).Error)
	assert.NoError(t, tx.Model(&observedMember1).Update("Hostname", "someunknownhost").Error)

	var desiredMemberRefetched model.ReplicaSetMember
	assert.NoError(t, tx.Model(&dbMongod).Related(&desiredMemberRefetched, "DesiredState").Error)
	assert.EqualValues(t, slave.Hostname, desiredMemberRefetched.Hostname)
	assert.EqualValues(t, "someunknownhost", observedMember1.Hostname)

	msg, err = monitor.compareStates(tx, dbMongod)
	assert.NoError(t, err)
	assert.EqualValues(t, true, msg.Mismatch, "unequal ReplicaSetMembers should result in a mismatch")

	tx.Rollback()
	tx = db.Begin()

	// Test with different number of members but same values
	observedMember2 := observedMember1
	observedMember2.Hostname = "anotherhost"
	assert.False(t, observedMember2.Hostname == observedMember1.Hostname, "error in test logic")
	observedMember2.ID = 0
	assert.NoError(t, tx.Create(&observedMember2).Error)

	msg, err = monitor.compareStates(tx, dbMongod)
	assert.NoError(t, err)
	assert.EqualValues(t, true, msg.Mismatch, "different sets of ReplicaSetMembers should result in a mismatch")

	tx.Rollback()

}

func TestMonitor_ReplicaSetMembersEquivalent(t *testing.T) {
	assert.True(t, ReplicaSetMembersEquivalent(msp.ReplicaSetMember{msp.HostPort{Hostname: "host1", Port: 100}, Priority: 1}, msp.ReplicaSetMember{msp.HostPort{Hostname: "host1", Port: 100}, Priority: 1}))
	assert.False(t, ReplicaSetMembersEquivalent(msp.ReplicaSetMember{msp.HostPort{Hostname: "host1", Port: 100}, Priority: 2}, msp.ReplicaSetMember{msp.HostPort{Hostname: "host1", Port: 100}, Priority: 1}))
	assert.False(t, ReplicaSetMembersEquivalent(msp.ReplicaSetMember{msp.HostPort{Hostname: "host1", Port: 100}, Priority: 1}, msp.ReplicaSetMember{msp.HostPort{Hostname: "host1", Port: 200}, Priority: 1}))
	assert.False(t, ReplicaSetMembersEquivalent(msp.ReplicaSetMember{msp.HostPort{Hostname: "host1", Port: 100}, Priority: 1}, msp.ReplicaSetMember{msp.HostPort{Hostname: "host2", Port: 100}, Priority: 1}))
	assert.False(t, ReplicaSetMembersEquivalent(msp.ReplicaSetMember{msp.HostPort{Hostname: "host1", Port: 100}, Priority: 1}, msp.ReplicaSetMember{msp.HostPort{Hostname: "host2", Port: 200}, Priority: 1}))
}
<|endoftext|>


<commit_before>/*
   Copyright The containerd 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 main

import (
	"fmt"
	"os"

	"github.com/containerd/containerd/cmd/ctr/app"
	"github.com/containerd/containerd/pkg/seed"
	"github.com/containerd/stargz-snapshotter/cmd/ctr-remote/commands"
	"github.com/urfave/cli"
)

func init() {
	seed.WithTimeAndRand()
}

func main() {
	customCommands := []cli.Command{commands.RpullCommand, commands.OptimizeCommand, commands.ConvertCommand}
	app := app.New()
	for i := range app.Commands {
		if app.Commands[i].Name == "images" {
			app.Commands[i].Subcommands = append(app.Commands[i].Subcommands, customCommands...)
			break
		}
	}
	if err := app.Run(os.Args); err != nil {
		fmt.Fprintf(os.Stderr, "ctr: %v\n", err)
		os.Exit(1)
	}
}
<commit_msg>Fix ctr-remote's subcommands don't override ctr's default<commit_after>/*
   Copyright The containerd 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 main

import (
	"fmt"
	"os"

	"github.com/containerd/containerd/cmd/ctr/app"
	"github.com/containerd/containerd/pkg/seed"
	"github.com/containerd/stargz-snapshotter/cmd/ctr-remote/commands"
	"github.com/urfave/cli"
)

func init() {
	seed.WithTimeAndRand()
}

func main() {
	customCommands := []cli.Command{commands.RpullCommand, commands.OptimizeCommand, commands.ConvertCommand}
	app := app.New()
	for i := range app.Commands {
		if app.Commands[i].Name == "images" {
			sc := map[string]cli.Command{}
			for _, subcmd := range customCommands {
				sc[subcmd.Name] = subcmd
			}

			// First, replace duplicated subcommands
			for j := range app.Commands[i].Subcommands {
				for name, subcmd := range sc {
					if name == app.Commands[i].Subcommands[j].Name {
						app.Commands[i].Subcommands[j] = subcmd
						delete(sc, name)
					}
				}
			}

			// Next, append all new sub commands
			for _, subcmd := range sc {
				app.Commands[i].Subcommands = append(app.Commands[i].Subcommands, subcmd)
			}
			break
		}
	}
	if err := app.Run(os.Args); err != nil {
		fmt.Fprintf(os.Stderr, "ctr: %v\n", err)
		os.Exit(1)
	}
}
<|endoftext|>


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

// Command demoserver serves an Upspin tree containing a series of boxes
// (files) containing Schrödinger's cats. The cats inside the boxes are in the
// superposition of dead and alive until a client does a Lookup of the box, at
// which point the superposition collapses and the reality of the cat's state
// is revealed.
//
// The purpose of this program is to demonstrate the implementation of a
// combined Upspin DirServer and StoreServer that serves dynamic content.
//
// See also: https://en.wikipedia.org/wiki/Schrödinger's_cat
package main

import (
	"flag"
	"fmt"
	"math/rand"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"

	"upspin.io/access"
	"upspin.io/cloud/https"
	"upspin.io/config"
	"upspin.io/errors"
	"upspin.io/flags"
	"upspin.io/log"
	"upspin.io/pack"
	"upspin.io/path"
	"upspin.io/rpc/dirserver"
	"upspin.io/rpc/storeserver"
	"upspin.io/serverutil"
	"upspin.io/upspin"

	_ "upspin.io/key/transports"
	_ "upspin.io/pack/eeintegrity"
)

func main() {
	rand.Seed(time.Now().UnixNano())

	// TODO(adg): make this flag a feature of package https.
	doTLS := flag.Bool("tls", true, "Serve TLS (HTTPS)")
	flags.Parse("config", "http", "https", "addr", "log", "letscache", "tls")

	addr := upspin.NetAddr(flags.NetAddr)
	ep := upspin.Endpoint{
		Transport: upspin.Remote,
		NetAddr:   addr,
	}
	cfg, err := config.FromFile(flags.Config)
	if err != nil {
		log.Fatal(err)
	}

	s, err := newServer(ep, cfg)
	if err != nil {
		log.Fatal(err)
	}

	http.Handle("/api/Store/", storeserver.New(cfg, s.StoreServer(), addr))
	http.Handle("/api/Dir/", dirserver.New(cfg, s.DirServer(), addr))

	if *doTLS {
		https.ListenAndServeFromFlags(nil, "dingus")
	} else {
		// TODO(adg): check that the address is localhost.
		log.Fatal(http.ListenAndServe(flags.HTTPAddr, nil))
	}
}

// box represents an opened box.
type box struct {
	*upspin.DirEntry
	data []byte
}

// server provides implementations of upspin.DirServer and upspin.StoreServer
// (accessed by calling the respective methods) that serve a tree containing
// many boxes containing Schrödinger's Cats.
type server struct {
	ep  upspin.Endpoint
	cfg upspin.Config

	accessEntry *upspin.DirEntry
	accessBytes []byte

	mu    sync.Mutex
	open  *sync.Cond // Broadcast when a box is opened for the first time.
	boxes []box
}

type dirServer struct {
	*server
}

type storeServer struct {
	*server
}

func (s *server) DirServer() upspin.DirServer {
	return &dirServer{s}
}

func (s *server) StoreServer() upspin.StoreServer {
	return &storeServer{s}
}

const (
	accessRef  = upspin.Reference(access.AccessFile)
	accessFile = "read,list:all\n"
)

var accessRefdata = upspin.Refdata{Reference: accessRef}

func newServer(ep upspin.Endpoint, cfg upspin.Config) (*server, error) {
	s := &server{
		ep:  ep,
		cfg: cfg,
	}
	s.open = sync.NewCond(&s.mu)

	var err error
	s.accessEntry, s.accessBytes, err = s.pack(access.AccessFile, []byte(accessFile))
	if err != nil {
		return nil, err
	}

	return s, nil
}

const packing = upspin.EEIntegrityPack

func (s *server) pack(filePath string, data []byte) (*upspin.DirEntry, []byte, error) {
	name := upspin.PathName(s.cfg.UserName()) + "/" + upspin.PathName(filePath)
	de := &upspin.DirEntry{
		Writer:     s.cfg.UserName(),
		Name:       name,
		SignedName: name,
		Packing:    packing,
		Time:       upspin.Now(),
		Sequence:   1,
	}

	bp, err := pack.Lookup(packing).Pack(s.cfg, de)
	if err != nil {
		return nil, nil, err
	}
	cipher, err := bp.Pack(data)
	if err != nil {
		return nil, nil, err
	}
	bp.SetLocation(upspin.Location{
		Endpoint:  s.ep,
		Reference: upspin.Reference(filePath),
	})
	return de, cipher, bp.Close()
}

// These methods implement upspin.Service.

func (s *server) Endpoint() upspin.Endpoint { return s.ep }
func (*server) Ping() bool                  { return true }
func (*server) Close()                      {}

// These methods implement upspin.Dialer.

func (s *storeServer) Dial(upspin.Config, upspin.Endpoint) (upspin.Service, error) { return s, nil }
func (s *dirServer) Dial(upspin.Config, upspin.Endpoint) (upspin.Service, error)   { return s, nil }

// These methods implement upspin.DirServer.

func (s *dirServer) Lookup(name upspin.PathName) (*upspin.DirEntry, error) {
	p, err := path.Parse(name)
	if err != nil {
		return nil, err
	}

	fp := p.FilePath()
	switch fp {
	case "": // Root directory.
		return &upspin.DirEntry{
			Name:       p.Path(),
			SignedName: p.Path(),
			Attr:       upspin.AttrDirectory,
			Time:       upspin.Now(),
		}, nil
	case access.AccessFile:
		return s.accessEntry, nil
	}

	n := matchBox(fp)
	if n < 0 {
		return nil, errors.E(name, errors.NotExist)
	}

	s.mu.Lock()
	defer s.mu.Unlock()

	total := len(s.boxes)
	if n > total {
		return nil, errors.E(name, errors.NotExist)
	}

	if n == total {
		// A new box is opened!
		de, data, err := s.pack(fp, randomState())
		if err != nil {
			return nil, errors.E(name, err)
		}
		s.boxes = append(s.boxes, box{de, data})
		s.open.Broadcast()
	}

	return s.boxes[n].DirEntry, nil
}

func (s *dirServer) Glob(pattern string) ([]*upspin.DirEntry, error) {
	return serverutil.Glob(pattern, s.Lookup, s.listDir)
}

func (s *dirServer) listDir(name upspin.PathName) ([]*upspin.DirEntry, error) {
	p, err := path.Parse(name)
	if err != nil {
		return nil, err
	}
	if p.User() != s.cfg.UserName() || p.FilePath() != "" {
		return nil, errors.E(name, errors.NotExist)
	}

	s.mu.Lock()
	defer s.mu.Unlock()

	var des []*upspin.DirEntry

	// List all the opened boxes in numerical order.
	for n := range s.boxes {
		de := s.boxes[n].DirEntry.Copy()
		de.MarkIncomplete()
		des = append(des, de)
	}

	// The final, closed box.
	des = append(des, s.closedBox(len(s.boxes)))

	return des, nil
}

func (s *dirServer) closedBox(n int) *upspin.DirEntry {
	name := upspin.PathName(s.cfg.UserName()) + "/" + upspin.PathName(fmtBox(n))
	return &upspin.DirEntry{
		Name:       name,
		SignedName: name,
		Attr:       upspin.AttrIncomplete,
		Time:       upspin.Now(),
		Writer:     s.cfg.UserName(),
	}
}

func (s *dirServer) Watch(name upspin.PathName, order int64, done <-chan struct{}) (<-chan upspin.Event, error) {
	p, err := path.Parse(name)
	if err != nil {
		return nil, err
	}
	if p.User() != s.cfg.UserName() {
		return nil, errors.E(name, errors.NotExist)
	}

	fp := p.FilePath()
	match := func(de *upspin.DirEntry) bool {
		return fp == "" || name == de.Name

	}

	n := int(order)
	if n < 0 {
		n = 0
	}
	isDone := func() bool {
		select {
		case <-done:
			return true
		default:
			return false
		}
	}
	events := make(chan upspin.Event)
	go func() {
		<-done
		s.open.Broadcast()
	}()
	go func() {
		defer close(events)
		for {
			s.mu.Lock()
			if n == len(s.boxes) {
				// Send the closed box.
				go func(n int) {
					if de := s.closedBox(n); match(de) {
						events <- upspin.Event{
							Entry: s.closedBox(n),
							Order: int64(n),
						}
					}
				}(n)
			}
			for !isDone() && n >= len(s.boxes) {
				s.open.Wait()
			}
			if isDone() {
				s.mu.Unlock()
				return
			}
			de := s.boxes[n].DirEntry
			s.mu.Unlock()

			// Send the next opened box.
			if match(de) {
				events <- upspin.Event{
					Entry: de,
					Order: int64(n),
				}
			}
			n++
		}
	}()
	return events, nil
}

func (s *dirServer) WhichAccess(name upspin.PathName) (*upspin.DirEntry, error) {
	return s.accessEntry, nil
}

// This method implements upspin.StoreServer.

func (s *storeServer) Get(ref upspin.Reference) ([]byte, *upspin.Refdata, []upspin.Location, error) {
	if ref == accessRef {
		return s.accessBytes, &accessRefdata, nil, nil
	}

	n := matchBox(string(ref))

	s.mu.Lock()
	defer s.mu.Unlock()

	if n < 0 || n >= len(s.boxes) {
		return nil, nil, nil, errors.E(errors.NotExist, errors.Errorf("unknown reference %q", ref))
	}

	return s.boxes[n].data, &upspin.Refdata{Reference: ref}, nil, nil
}

// The DirServer and StoreServer methods below are not implemented.

var errNotImplemented = errors.E(errors.Permission, errors.Str("method not implemented: dingus is read-only"))

func (*dirServer) Put(entry *upspin.DirEntry) (*upspin.DirEntry, error) {
	return nil, errNotImplemented
}

func (*dirServer) Delete(name upspin.PathName) (*upspin.DirEntry, error) {
	return nil, errNotImplemented
}

func (*storeServer) Put(data []byte) (*upspin.Refdata, error) {
	return nil, errNotImplemented
}

func (*storeServer) Delete(ref upspin.Reference) error {
	return errNotImplemented
}

// Utility functions.

const boxName = "box"

func fmtBox(n int) string {
	return fmt.Sprintf("%s%d", boxName, n)
}

func matchBox(filePath string) int {
	if !strings.HasPrefix(filePath, boxName) {
		return -1
	}
	n := filePath[len(boxName):]
	i, _ := strconv.ParseInt(n, 10, 32)
	if i < 0 || fmtBox(int(i)) != filePath {
		return -1
	}
	return int(i)
}

var states = [][]byte{
	[]byte("A dead cat.\n"),
	[]byte("A live cat.\n"),
}

func randomState() []byte {
	return states[rand.Intn(2)]
}
<commit_msg>cloud/https, flag: add -insecure flag to serve by insecure HTTP<commit_after>// Copyright 2017 The Upspin Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Command demoserver serves an Upspin tree containing a series of boxes
// (files) containing Schrödinger's cats. The cats inside the boxes are in the
// superposition of dead and alive until a client does a Lookup of the box, at
// which point the superposition collapses and the reality of the cat's state
// is revealed.
//
// The purpose of this program is to demonstrate the implementation of a
// combined Upspin DirServer and StoreServer that serves dynamic content.
//
// See also: https://en.wikipedia.org/wiki/Schrödinger's_cat
package main

import (
	"fmt"
	"math/rand"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"

	"upspin.io/access"
	"upspin.io/cloud/https"
	"upspin.io/config"
	"upspin.io/errors"
	"upspin.io/flags"
	"upspin.io/log"
	"upspin.io/pack"
	"upspin.io/path"
	"upspin.io/rpc/dirserver"
	"upspin.io/rpc/storeserver"
	"upspin.io/serverutil"
	"upspin.io/upspin"

	_ "upspin.io/key/transports"
	_ "upspin.io/pack/eeintegrity"
)

func main() {
	rand.Seed(time.Now().UnixNano())
	flags.Parse("config", "insecure", "http", "https", "addr", "log", "letscache", "tls")

	addr := upspin.NetAddr(flags.NetAddr)
	ep := upspin.Endpoint{
		Transport: upspin.Remote,
		NetAddr:   addr,
	}
	cfg, err := config.FromFile(flags.Config)
	if err != nil {
		log.Fatal(err)
	}

	s, err := newServer(ep, cfg)
	if err != nil {
		log.Fatal(err)
	}

	http.Handle("/api/Store/", storeserver.New(cfg, s.StoreServer(), addr))
	http.Handle("/api/Dir/", dirserver.New(cfg, s.DirServer(), addr))

	https.ListenAndServeFromFlags(nil, "demoserver")
}

// box represents an opened box.
type box struct {
	*upspin.DirEntry
	data []byte
}

// server provides implementations of upspin.DirServer and upspin.StoreServer
// (accessed by calling the respective methods) that serve a tree containing
// many boxes containing Schrödinger's Cats.
type server struct {
	ep  upspin.Endpoint
	cfg upspin.Config

	accessEntry *upspin.DirEntry
	accessBytes []byte

	mu    sync.Mutex
	open  *sync.Cond // Broadcast when a box is opened for the first time.
	boxes []box
}

type dirServer struct {
	*server
}

type storeServer struct {
	*server
}

func (s *server) DirServer() upspin.DirServer {
	return &dirServer{s}
}

func (s *server) StoreServer() upspin.StoreServer {
	return &storeServer{s}
}

const (
	accessRef  = upspin.Reference(access.AccessFile)
	accessFile = "read,list:all\n"
)

var accessRefdata = upspin.Refdata{Reference: accessRef}

func newServer(ep upspin.Endpoint, cfg upspin.Config) (*server, error) {
	s := &server{
		ep:  ep,
		cfg: cfg,
	}
	s.open = sync.NewCond(&s.mu)

	var err error
	s.accessEntry, s.accessBytes, err = s.pack(access.AccessFile, []byte(accessFile))
	if err != nil {
		return nil, err
	}

	return s, nil
}

const packing = upspin.EEIntegrityPack

func (s *server) pack(filePath string, data []byte) (*upspin.DirEntry, []byte, error) {
	name := upspin.PathName(s.cfg.UserName()) + "/" + upspin.PathName(filePath)
	de := &upspin.DirEntry{
		Writer:     s.cfg.UserName(),
		Name:       name,
		SignedName: name,
		Packing:    packing,
		Time:       upspin.Now(),
		Sequence:   1,
	}

	bp, err := pack.Lookup(packing).Pack(s.cfg, de)
	if err != nil {
		return nil, nil, err
	}
	cipher, err := bp.Pack(data)
	if err != nil {
		return nil, nil, err
	}
	bp.SetLocation(upspin.Location{
		Endpoint:  s.ep,
		Reference: upspin.Reference(filePath),
	})
	return de, cipher, bp.Close()
}

// These methods implement upspin.Service.

func (s *server) Endpoint() upspin.Endpoint { return s.ep }
func (*server) Ping() bool                  { return true }
func (*server) Close()                      {}

// These methods implement upspin.Dialer.

func (s *storeServer) Dial(upspin.Config, upspin.Endpoint) (upspin.Service, error) { return s, nil }
func (s *dirServer) Dial(upspin.Config, upspin.Endpoint) (upspin.Service, error)   { return s, nil }

// These methods implement upspin.DirServer.

func (s *dirServer) Lookup(name upspin.PathName) (*upspin.DirEntry, error) {
	p, err := path.Parse(name)
	if err != nil {
		return nil, err
	}

	fp := p.FilePath()
	switch fp {
	case "": // Root directory.
		return &upspin.DirEntry{
			Name:       p.Path(),
			SignedName: p.Path(),
			Attr:       upspin.AttrDirectory,
			Time:       upspin.Now(),
		}, nil
	case access.AccessFile:
		return s.accessEntry, nil
	}

	n := matchBox(fp)
	if n < 0 {
		return nil, errors.E(name, errors.NotExist)
	}

	s.mu.Lock()
	defer s.mu.Unlock()

	total := len(s.boxes)
	if n > total {
		return nil, errors.E(name, errors.NotExist)
	}

	if n == total {
		// A new box is opened!
		de, data, err := s.pack(fp, randomState())
		if err != nil {
			return nil, errors.E(name, err)
		}
		s.boxes = append(s.boxes, box{de, data})
		s.open.Broadcast()
	}

	return s.boxes[n].DirEntry, nil
}

func (s *dirServer) Glob(pattern string) ([]*upspin.DirEntry, error) {
	return serverutil.Glob(pattern, s.Lookup, s.listDir)
}

func (s *dirServer) listDir(name upspin.PathName) ([]*upspin.DirEntry, error) {
	p, err := path.Parse(name)
	if err != nil {
		return nil, err
	}
	if p.User() != s.cfg.UserName() || p.FilePath() != "" {
		return nil, errors.E(name, errors.NotExist)
	}

	s.mu.Lock()
	defer s.mu.Unlock()

	var des []*upspin.DirEntry

	// List all the opened boxes in numerical order.
	for n := range s.boxes {
		de := s.boxes[n].DirEntry.Copy()
		de.MarkIncomplete()
		des = append(des, de)
	}

	// The final, closed box.
	des = append(des, s.closedBox(len(s.boxes)))

	return des, nil
}

func (s *dirServer) closedBox(n int) *upspin.DirEntry {
	name := upspin.PathName(s.cfg.UserName()) + "/" + upspin.PathName(fmtBox(n))
	return &upspin.DirEntry{
		Name:       name,
		SignedName: name,
		Attr:       upspin.AttrIncomplete,
		Time:       upspin.Now(),
		Writer:     s.cfg.UserName(),
	}
}

func (s *dirServer) Watch(name upspin.PathName, order int64, done <-chan struct{}) (<-chan upspin.Event, error) {
	p, err := path.Parse(name)
	if err != nil {
		return nil, err
	}
	if p.User() != s.cfg.UserName() {
		return nil, errors.E(name, errors.NotExist)
	}

	fp := p.FilePath()
	match := func(de *upspin.DirEntry) bool {
		return fp == "" || name == de.Name

	}

	n := int(order)
	if n < 0 {
		n = 0
	}
	isDone := func() bool {
		select {
		case <-done:
			return true
		default:
			return false
		}
	}
	events := make(chan upspin.Event)
	go func() {
		<-done
		s.open.Broadcast()
	}()
	go func() {
		defer close(events)
		for {
			s.mu.Lock()
			if n == len(s.boxes) {
				// Send the closed box.
				go func(n int) {
					if de := s.closedBox(n); match(de) {
						events <- upspin.Event{
							Entry: s.closedBox(n),
							Order: int64(n),
						}
					}
				}(n)
			}
			for !isDone() && n >= len(s.boxes) {
				s.open.Wait()
			}
			if isDone() {
				s.mu.Unlock()
				return
			}
			de := s.boxes[n].DirEntry
			s.mu.Unlock()

			// Send the next opened box.
			if match(de) {
				events <- upspin.Event{
					Entry: de,
					Order: int64(n),
				}
			}
			n++
		}
	}()
	return events, nil
}

func (s *dirServer) WhichAccess(name upspin.PathName) (*upspin.DirEntry, error) {
	return s.accessEntry, nil
}

// This method implements upspin.StoreServer.

func (s *storeServer) Get(ref upspin.Reference) ([]byte, *upspin.Refdata, []upspin.Location, error) {
	if ref == accessRef {
		return s.accessBytes, &accessRefdata, nil, nil
	}

	n := matchBox(string(ref))

	s.mu.Lock()
	defer s.mu.Unlock()

	if n < 0 || n >= len(s.boxes) {
		return nil, nil, nil, errors.E(errors.NotExist, errors.Errorf("unknown reference %q", ref))
	}

	return s.boxes[n].data, &upspin.Refdata{Reference: ref}, nil, nil
}

// The DirServer and StoreServer methods below are not implemented.

var errNotImplemented = errors.E(errors.Permission, errors.Str("method not implemented: dingus is read-only"))

func (*dirServer) Put(entry *upspin.DirEntry) (*upspin.DirEntry, error) {
	return nil, errNotImplemented
}

func (*dirServer) Delete(name upspin.PathName) (*upspin.DirEntry, error) {
	return nil, errNotImplemented
}

func (*storeServer) Put(data []byte) (*upspin.Refdata, error) {
	return nil, errNotImplemented
}

func (*storeServer) Delete(ref upspin.Reference) error {
	return errNotImplemented
}

// Utility functions.

const boxName = "box"

func fmtBox(n int) string {
	return fmt.Sprintf("%s%d", boxName, n)
}

func matchBox(filePath string) int {
	if !strings.HasPrefix(filePath, boxName) {
		return -1
	}
	n := filePath[len(boxName):]
	i, _ := strconv.ParseInt(n, 10, 32)
	if i < 0 || fmtBox(int(i)) != filePath {
		return -1
	}
	return int(i)
}

var states = [][]byte{
	[]byte("A dead cat.\n"),
	[]byte("A live cat.\n"),
}

func randomState() []byte {
	return states[rand.Intn(2)]
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"flag"
	"fmt"
	"go/format"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"

	typeconv "github.com/haya14busa/go-typeconv"

	"golang.org/x/tools/go/loader"
)

type option struct {
	write  bool
	doDiff bool
	rules  strslice
}

func main() {
	opt := &option{}
	flag.BoolVar(&opt.write, "w", false, "write result to (source) file instead of stdout")
	flag.BoolVar(&opt.doDiff, "d", false, "display diffs instead of rewriting files")
	flag.Var(&opt.rules, "r", "type conversion rules currently just for type conversion of binary expression (e.g., 'int -> uint32')")
	flag.Parse()
	if err := run(os.Stdout, flag.Args(), opt); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func run(w io.Writer, args []string, opt *option) error {
	if err := addRules(opt.rules); err != nil {
		return err
	}
	prog, typeErrs, err := typeconv.Load(loader.Config{}, args)
	if err != nil {
		return err
	}
	for _, pkg := range prog.InitialPackages() {
		for _, f := range pkg.Files {
			filename := prog.Fset.File(f.Pos()).Name()
			if err := typeconv.RewriteFile(prog.Fset, f, pkg, typeErrs); err != nil {
				return err
			}
			buf := new(bytes.Buffer)
			if err := format.Node(buf, prog.Fset, f); err != nil {
				return err
			}
			res := buf.Bytes()
			in, err := os.Open(filename)
			if err != nil {
				return err
			}
			src, err := ioutil.ReadAll(in)
			if err != nil {
				return err
			}
			if !bytes.Equal(src, res) {
				if opt.write {
					fh, err := os.Create(filename)
					if err != nil {
						return err
					}
					fh.Write(res)
					fh.Close()
				}
				if opt.doDiff {
					data, err := diff(src, res)
					if err != nil {
						return fmt.Errorf("computing diff: %s", err)
					}
					fmt.Fprintf(w, "diff %s gotypeconv/%s\n", filename, filename)
					w.Write(data)
				}
			}
			if !opt.write && !opt.doDiff {
				w.Write(res)
			}
		}
	}
	return nil
}

func addRules(rules []string) error {
	for _, r := range rules {
		f := strings.Split(r, "->")
		if len(f) != 2 {
			return fmt.Errorf("type conversion rule must be the form 'from -> to': %v", r)
		}
		from, to := strings.TrimSpace(f[0]), strings.TrimSpace(f[1])
		typeconv.DefaultRule.Add(from, to)
	}
	return nil
}

// copied and modified from $GOPATH/src/github.com/golang/go/src/cmd/gofmt/gofmt.go
//
// Copyright 2009 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.
func diff(b1, b2 []byte) (data []byte, err error) {
	f1, err := ioutil.TempFile("", "gotypeconv")
	if err != nil {
		return
	}
	defer os.Remove(f1.Name())
	defer f1.Close()

	f2, err := ioutil.TempFile("", "gotypeconv")
	if err != nil {
		return
	}
	defer os.Remove(f2.Name())
	defer f2.Close()

	f1.Write(b1)
	f2.Write(b2)

	data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput()
	if len(data) > 0 {
		// diff exits with a non-zero status when the files don't match.
		// Ignore that failure as long as we get output.
		err = nil
	}
	return

}

type strslice []string

func (ss *strslice) String() string {
	return fmt.Sprintf("%v", *ss)
}

func (ss *strslice) Set(value string) error {
	*ss = append(*ss, value)
	return nil
}
<commit_msg>cmd: use buffered io<commit_after>package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"go/format"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"

	typeconv "github.com/haya14busa/go-typeconv"

	"golang.org/x/tools/go/loader"
)

type option struct {
	write  bool
	doDiff bool
	rules  strslice
}

func main() {
	opt := &option{}
	flag.BoolVar(&opt.write, "w", false, "write result to (source) file instead of stdout")
	flag.BoolVar(&opt.doDiff, "d", false, "display diffs instead of rewriting files")
	flag.Var(&opt.rules, "r", "type conversion rules currently just for type conversion of binary expression (e.g., 'int -> uint32')")
	flag.Parse()
	out := bufio.NewWriter(os.Stdout)
	defer out.Flush()
	if err := run(out, flag.Args(), opt); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func run(w io.Writer, args []string, opt *option) error {
	if err := addRules(opt.rules); err != nil {
		return err
	}
	prog, typeErrs, err := typeconv.Load(loader.Config{}, args)
	if err != nil {
		return err
	}
	for _, pkg := range prog.InitialPackages() {
		for _, f := range pkg.Files {
			filename := prog.Fset.File(f.Pos()).Name()
			if err := typeconv.RewriteFile(prog.Fset, f, pkg, typeErrs); err != nil {
				return err
			}
			buf := new(bytes.Buffer)
			if err := format.Node(buf, prog.Fset, f); err != nil {
				return err
			}
			res := buf.Bytes()
			in, err := os.Open(filename)
			if err != nil {
				return err
			}
			src, err := ioutil.ReadAll(in)
			if err != nil {
				return err
			}
			if !bytes.Equal(src, res) {
				if opt.write {
					fh, err := os.Create(filename)
					if err != nil {
						return err
					}
					fh.Write(res)
					fh.Close()
				}
				if opt.doDiff {
					data, err := diff(src, res)
					if err != nil {
						return fmt.Errorf("computing diff: %s", err)
					}
					fmt.Fprintf(w, "diff %s gotypeconv/%s\n", filename, filename)
					w.Write(data)
				}
			}
			if !opt.write && !opt.doDiff {
				w.Write(res)
			}
		}
	}
	return nil
}

func addRules(rules []string) error {
	for _, r := range rules {
		f := strings.Split(r, "->")
		if len(f) != 2 {
			return fmt.Errorf("type conversion rule must be the form 'from -> to': %v", r)
		}
		from, to := strings.TrimSpace(f[0]), strings.TrimSpace(f[1])
		typeconv.DefaultRule.Add(from, to)
	}
	return nil
}

// copied and modified from $GOPATH/src/github.com/golang/go/src/cmd/gofmt/gofmt.go
//
// Copyright 2009 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.
func diff(b1, b2 []byte) (data []byte, err error) {
	f1, err := ioutil.TempFile("", "gotypeconv")
	if err != nil {
		return
	}
	defer os.Remove(f1.Name())
	defer f1.Close()

	f2, err := ioutil.TempFile("", "gotypeconv")
	if err != nil {
		return
	}
	defer os.Remove(f2.Name())
	defer f2.Close()

	f1.Write(b1)
	f2.Write(b2)

	data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput()
	if len(data) > 0 {
		// diff exits with a non-zero status when the files don't match.
		// Ignore that failure as long as we get output.
		err = nil
	}
	return

}

type strslice []string

func (ss *strslice) String() string {
	return fmt.Sprintf("%v", *ss)
}

func (ss *strslice) Set(value string) error {
	*ss = append(*ss, value)
	return nil
}
<|endoftext|>


<commit_before>/* Copyright (c) 2014-2015, Daniel Martí <mvdan@mvdan.cc> */
/* See LICENSE for licensing information */

package main

import (
	"bytes"
	"flag"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"

	"github.com/mvdan/jutgelint"
)

var (
	lang jutgelint.Lang = jutgelint.LangAuto
)

func init() {
	flag.Var(&lang, "lang", "Language to use (auto, c++, go)")
}

func main() {
	flag.Parse()
	args := flag.Args()

	if len(args) > 2 {
		flag.Usage()
	}

	in := os.Stdin
	out := os.Stdout

	if len(args) >= 1 {
		f, err := os.Open(args[0])
		if err != nil {
			log.Fatalf("Cannot open file: %v", err)
		}
		if lang == jutgelint.LangAuto {
			ext := filepath.Ext(args[0])
			lang.Set(ext[1:])
		}
		in = f
	}
	if len(args) >= 2 {
		f, err := os.Create(args[1])
		if err != nil {
			log.Fatalf("Cannot open file: %v", err)
		}
		out = f
	}

	code, err := ioutil.ReadAll(in)
	if err != nil {
		log.Fatalf("Error when reading code: %v", err)
	}
	var json bytes.Buffer
	if err := jutgelint.EncodeJsonFromCode(lang, bytes.NewReader(code), &json); err != nil {
		log.Fatalf("Could not translate code into json: %v", err)
	}

	warns, err := jutgelint.RunChecker(&json)
	if err != nil {
		log.Fatalf("Error when running the checker: %v", err)
	}
	jutgelint.CommentCode(warns, bytes.NewReader(code), out)
}
<commit_msg>Better usage function<commit_after>/* Copyright (c) 2014-2015, Daniel Martí <mvdan@mvdan.cc> */
/* See LICENSE for licensing information */

package main

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"

	"github.com/mvdan/jutgelint"
)

var (
	lang jutgelint.Lang = jutgelint.LangAuto
)

func init() {
	flag.Var(&lang, "lang", "Language to use (auto, c++, go)")
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: jutgelint [input] [output]\n\n")
		fmt.Fprintf(os.Stderr, "The input and output files default to standard input and standard output\n")
		fmt.Fprintf(os.Stderr, "if none are specified.\n\n")
		fmt.Fprintf(os.Stderr, "Options:\n")
		flag.PrintDefaults()
	}
}

func main() {
	flag.Parse()
	args := flag.Args()

	if len(args) > 2 {
		flag.Usage()
	}

	in := os.Stdin
	out := os.Stdout

	if len(args) >= 1 {
		f, err := os.Open(args[0])
		if err != nil {
			log.Fatalf("Cannot open file: %v", err)
		}
		if lang == jutgelint.LangAuto {
			ext := filepath.Ext(args[0])
			lang.Set(ext[1:])
		}
		in = f
	}
	if len(args) >= 2 {
		f, err := os.Create(args[1])
		if err != nil {
			log.Fatalf("Cannot open file: %v", err)
		}
		out = f
	}

	code, err := ioutil.ReadAll(in)
	if err != nil {
		log.Fatalf("Error when reading code: %v", err)
	}
	var json bytes.Buffer
	if err := jutgelint.EncodeJsonFromCode(lang, bytes.NewReader(code), &json); err != nil {
		log.Fatalf("Could not translate code into json: %v", err)
	}

	warns, err := jutgelint.RunChecker(&json)
	if err != nil {
		log.Fatalf("Error when running the checker: %v", err)
	}
	jutgelint.CommentCode(warns, bytes.NewReader(code), out)
}
<|endoftext|>


<commit_before>// Copyright 2015 The Prometheus 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.

// The main package for the Prometheus server executeable.
package main

import (
	"bytes"
	"flag"
	"fmt"
	_ "net/http/pprof" // Comment this line to disable pprof endpoint.
	"os"
	"os/signal"
	"strings"
	"syscall"
	"text/template"
	"time"

	"github.com/prometheus/log"

	"github.com/prometheus/client_golang/prometheus"

	"github.com/prometheus/prometheus/config"
	"github.com/prometheus/prometheus/notification"
	"github.com/prometheus/prometheus/promql"
	"github.com/prometheus/prometheus/retrieval"
	"github.com/prometheus/prometheus/rules"
	"github.com/prometheus/prometheus/storage"
	"github.com/prometheus/prometheus/storage/local"
	"github.com/prometheus/prometheus/storage/remote"
	"github.com/prometheus/prometheus/version"
	"github.com/prometheus/prometheus/web"
)

func main() {
	os.Exit(Main())
}

func Main() int {
	if err := parse(os.Args[1:]); err != nil {
		return 2
	}

	printVersion()
	if cfg.printVersion {
		return 0
	}

	var (
		memStorage     = local.NewMemorySeriesStorage(&cfg.storage)
		remoteStorage  = remote.New(&cfg.remote)
		sampleAppender = storage.Fanout{memStorage}
	)
	if remoteStorage != nil {
		sampleAppender = append(sampleAppender, remoteStorage)
	}

	var (
		notificationHandler = notification.NewNotificationHandler(&cfg.notification)
		targetManager       = retrieval.NewTargetManager(sampleAppender)
		queryEngine         = promql.NewEngine(memStorage, &cfg.queryEngine)
	)

	ruleManager := rules.NewManager(&rules.ManagerOptions{
		SampleAppender:      sampleAppender,
		NotificationHandler: notificationHandler,
		QueryEngine:         queryEngine,
		ExternalURL:         cfg.web.ExternalURL,
	})

	flags := map[string]string{}
	cfg.fs.VisitAll(func(f *flag.Flag) {
		flags[f.Name] = f.Value.String()
	})

	status := &web.PrometheusStatus{
		TargetPools: targetManager.Pools,
		Rules:       ruleManager.Rules,
		Flags:       flags,
		Birth:       time.Now(),
	}

	webHandler := web.New(memStorage, queryEngine, ruleManager, status, &cfg.web)

	if !reloadConfig(cfg.configFile, status, targetManager, ruleManager) {
		return 1
	}

	// Wait for reload or termination signals. Start the handler for SIGHUP as
	// early as possible, but ignore it until we are ready to handle reloading
	// our config.
	hup := make(chan os.Signal)
	hupReady := make(chan bool)
	signal.Notify(hup, syscall.SIGHUP)
	go func() {
		<-hupReady
		for {
			select {
			case <-hup:
			case <-webHandler.Reload():
			}
			reloadConfig(cfg.configFile, status, targetManager, ruleManager)
		}
	}()

	// Start all components.
	if err := memStorage.Start(); err != nil {
		log.Errorln("Error opening memory series storage:", err)
		return 1
	}
	defer func() {
		if err := memStorage.Stop(); err != nil {
			log.Errorln("Error stopping storage:", err)
		}
	}()

	if remoteStorage != nil {
		prometheus.MustRegister(remoteStorage)

		go remoteStorage.Run()
		defer remoteStorage.Stop()
	}
	// The storage has to be fully initialized before registering.
	prometheus.MustRegister(memStorage)
	prometheus.MustRegister(notificationHandler)

	go ruleManager.Run()
	defer ruleManager.Stop()

	go notificationHandler.Run()
	defer notificationHandler.Stop()

	go targetManager.Run()
	defer targetManager.Stop()

	defer queryEngine.Stop()

	go webHandler.Run()

	// Wait for reload or termination signals.
	close(hupReady) // Unblock SIGHUP handler.

	term := make(chan os.Signal)
	signal.Notify(term, os.Interrupt, syscall.SIGTERM)
	select {
	case <-term:
		log.Warn("Received SIGTERM, exiting gracefully...")
	case <-webHandler.Quit():
		log.Warn("Received termination request via web service, exiting gracefully...")
	}

	close(hup)

	log.Info("See you next time!")
	return 0
}

// Reloadable things can change their internal state to match a new config
// and handle failure gracefully.
type Reloadable interface {
	ApplyConfig(*config.Config) bool
}

func reloadConfig(filename string, rls ...Reloadable) bool {
	log.Infof("Loading configuration file %s", filename)

	conf, err := config.LoadFile(filename)
	if err != nil {
		log.Errorf("Couldn't load configuration (-config.file=%s): %v", filename, err)
		log.Errorf("Note: The configuration format has changed with version 0.14. Please see the documentation (http://prometheus.io/docs/operating/configuration/) and the provided configuration migration tool (https://github.com/prometheus/migrate).")
		return false
	}
	success := true

	for _, rl := range rls {
		success = success && rl.ApplyConfig(conf)
	}
	return success
}

var versionInfoTmpl = `
prometheus, version {{.version}} (branch: {{.branch}}, revision: {{.revision}})
  build user:       {{.buildUser}}
  build date:       {{.buildDate}}
  go version:       {{.goVersion}}
`

func printVersion() {
	t := template.Must(template.New("version").Parse(versionInfoTmpl))

	var buf bytes.Buffer
	if err := t.ExecuteTemplate(&buf, "version", version.Map); err != nil {
		panic(err)
	}
	fmt.Fprintln(os.Stdout, strings.TrimSpace(buf.String()))
}
<commit_msg>Fix loop-reloading on shutdown<commit_after>// Copyright 2015 The Prometheus 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.

// The main package for the Prometheus server executeable.
package main

import (
	"bytes"
	"flag"
	"fmt"
	_ "net/http/pprof" // Comment this line to disable pprof endpoint.
	"os"
	"os/signal"
	"strings"
	"syscall"
	"text/template"
	"time"

	"github.com/prometheus/log"

	"github.com/prometheus/client_golang/prometheus"

	"github.com/prometheus/prometheus/config"
	"github.com/prometheus/prometheus/notification"
	"github.com/prometheus/prometheus/promql"
	"github.com/prometheus/prometheus/retrieval"
	"github.com/prometheus/prometheus/rules"
	"github.com/prometheus/prometheus/storage"
	"github.com/prometheus/prometheus/storage/local"
	"github.com/prometheus/prometheus/storage/remote"
	"github.com/prometheus/prometheus/version"
	"github.com/prometheus/prometheus/web"
)

func main() {
	os.Exit(Main())
}

func Main() int {
	if err := parse(os.Args[1:]); err != nil {
		return 2
	}

	printVersion()
	if cfg.printVersion {
		return 0
	}

	var (
		memStorage     = local.NewMemorySeriesStorage(&cfg.storage)
		remoteStorage  = remote.New(&cfg.remote)
		sampleAppender = storage.Fanout{memStorage}
	)
	if remoteStorage != nil {
		sampleAppender = append(sampleAppender, remoteStorage)
	}

	var (
		notificationHandler = notification.NewNotificationHandler(&cfg.notification)
		targetManager       = retrieval.NewTargetManager(sampleAppender)
		queryEngine         = promql.NewEngine(memStorage, &cfg.queryEngine)
	)

	ruleManager := rules.NewManager(&rules.ManagerOptions{
		SampleAppender:      sampleAppender,
		NotificationHandler: notificationHandler,
		QueryEngine:         queryEngine,
		ExternalURL:         cfg.web.ExternalURL,
	})

	flags := map[string]string{}
	cfg.fs.VisitAll(func(f *flag.Flag) {
		flags[f.Name] = f.Value.String()
	})

	status := &web.PrometheusStatus{
		TargetPools: targetManager.Pools,
		Rules:       ruleManager.Rules,
		Flags:       flags,
		Birth:       time.Now(),
	}

	webHandler := web.New(memStorage, queryEngine, ruleManager, status, &cfg.web)

	if !reloadConfig(cfg.configFile, status, targetManager, ruleManager) {
		return 1
	}

	// Wait for reload or termination signals. Start the handler for SIGHUP as
	// early as possible, but ignore it until we are ready to handle reloading
	// our config.
	hup := make(chan os.Signal)
	hupReady := make(chan bool)
	signal.Notify(hup, syscall.SIGHUP)
	go func() {
		<-hupReady
		for {
			select {
			case <-hup:
			case <-webHandler.Reload():
			}
			reloadConfig(cfg.configFile, status, targetManager, ruleManager)
		}
	}()

	// Start all components.
	if err := memStorage.Start(); err != nil {
		log.Errorln("Error opening memory series storage:", err)
		return 1
	}
	defer func() {
		if err := memStorage.Stop(); err != nil {
			log.Errorln("Error stopping storage:", err)
		}
	}()

	if remoteStorage != nil {
		prometheus.MustRegister(remoteStorage)

		go remoteStorage.Run()
		defer remoteStorage.Stop()
	}
	// The storage has to be fully initialized before registering.
	prometheus.MustRegister(memStorage)
	prometheus.MustRegister(notificationHandler)

	go ruleManager.Run()
	defer ruleManager.Stop()

	go notificationHandler.Run()
	defer notificationHandler.Stop()

	go targetManager.Run()
	defer targetManager.Stop()

	defer queryEngine.Stop()

	go webHandler.Run()

	// Wait for reload or termination signals.
	close(hupReady) // Unblock SIGHUP handler.

	term := make(chan os.Signal)
	signal.Notify(term, os.Interrupt, syscall.SIGTERM)
	select {
	case <-term:
		log.Warn("Received SIGTERM, exiting gracefully...")
	case <-webHandler.Quit():
		log.Warn("Received termination request via web service, exiting gracefully...")
	}

	log.Info("See you next time!")
	return 0
}

// Reloadable things can change their internal state to match a new config
// and handle failure gracefully.
type Reloadable interface {
	ApplyConfig(*config.Config) bool
}

func reloadConfig(filename string, rls ...Reloadable) bool {
	log.Infof("Loading configuration file %s", filename)

	conf, err := config.LoadFile(filename)
	if err != nil {
		log.Errorf("Couldn't load configuration (-config.file=%s): %v", filename, err)
		log.Errorf("Note: The configuration format has changed with version 0.14. Please see the documentation (http://prometheus.io/docs/operating/configuration/) and the provided configuration migration tool (https://github.com/prometheus/migrate).")
		return false
	}
	success := true

	for _, rl := range rls {
		success = success && rl.ApplyConfig(conf)
	}
	return success
}

var versionInfoTmpl = `
prometheus, version {{.version}} (branch: {{.branch}}, revision: {{.revision}})
  build user:       {{.buildUser}}
  build date:       {{.buildDate}}
  go version:       {{.goVersion}}
`

func printVersion() {
	t := template.Must(template.New("version").Parse(versionInfoTmpl))

	var buf bytes.Buffer
	if err := t.ExecuteTemplate(&buf, "version", version.Map); err != nil {
		panic(err)
	}
	fmt.Fprintln(os.Stdout, strings.TrimSpace(buf.String()))
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	util "github.com/CSUNetSec/gobgpdump"
	mrt "github.com/CSUNetSec/protoparse/protocol/mrt"
	//rib "github.com/CSUNetSec/protoparse/protocol/rib"
	"os"
)

func main() {
	if len(os.Args) < 2 {
		fmt.Println("Not enough arguments")
		return
	}

	fd, err := os.Open(os.Args[1])
	if err != nil {
		fmt.Printf("Error opening file: %s\n", os.Args[1])
		return
	}
	defer fd.Close()
	scanner := util.GetMRTScanner(fd)
	ribBuffer := make([]byte, 2<<32)
	scanner.Buffer(ribBuffer, cap(ribBuffer))

	scanner.Scan()
	buf := scanner.Bytes()

	mrth := mrt.NewMrtHdrBuf(buf)
	index, err := mrth.Parse()
	if err != nil {
		fmt.Printf("MRT parse error: %s\n", err)
		return
	}
	_, err = index.Parse()
	if err != nil {
		fmt.Printf("Index parse error: %s\n", err)
		return
	}

	fmt.Printf("Index:\n%s\n", index)

	ribEnts := 0
	for scanner.Scan() {
		ribEnts++
		mrth = mrt.NewMrtHdrBuf(scanner.Bytes())
		ribH, err := mrth.Parse()
		if err != nil {
			fmt.Printf("Message %d MRT ERROR: %s\n", ribEnts, err)
			break
		}
		_, err = ribH.Parse()
		if err != nil {
			fmt.Printf("Message %d RIB ERROR: %s\n", ribEnts, err)
			break
		}
	}

	fmt.Printf("RIB entries: %d\n", ribEnts)

	if scanner.Err() != nil {
		fmt.Printf("Scanner error: %s\n", scanner.Err())
	}
}
<commit_msg>Removed testing tool ribdump<commit_after><|endoftext|>


<commit_before>// Copyright 2017 The Upspin 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 main

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"testing"

	"upspin.io/test/testutil"
	"upspin.io/upbox"
	"upspin.io/upspin"
)

var allCmdTests = []*[]cmdTest{
	&basicCmdTests,
	&cpTests,
	&globTests,
	&keygenTests,
	&shareTests,
	&suffixedUserTests,
}

// TestCommands runs the tests defined in cmdTests as subtests.
func TestCommands(t *testing.T) {
	// Set up upbox.
	portString, err := testutil.PickPort()
	if err != nil {
		t.Fatal(err)
	}
	port, _ := strconv.Atoi(portString)
	schema, err := upbox.SchemaFromYAML(upboxSchema, port)
	if err != nil {
		t.Fatalf("setting up schema: %v", err)
	}
	err = schema.Start()
	if err != nil {
		t.Fatalf("starting schema: %v", err)
	}

	// Loop over the tests in sequence, building state as we go.
	for _, testSuite := range allCmdTests {
		for _, test := range *testSuite {
			// We create a runner for each cmdTest so the Config and State
			// are constructed from the environment each time.
			r := &runner{
				fs:     flag.NewFlagSet(test.name, flag.PanicOnError), // panic if there's trouble.
				schema: schema,
			}
			state, _, ok := setup(r.fs, []string{"-config=" + r.config(test.user), "test"})
			if !ok {
				t.Fatal("setup failed; bad arg list?")
			}
			r.state = state
			t.Run(test.name, r.run(&test))
		}
	}

	// Tear down upbox.
	schema.Stop()
}

// TODO: Loop over server implementations?

const upboxSchema = `
users:
  - name: ann@example.com
  - name: chris@example.com
  - name: kelly@example.com
  - name: lee@example.com
servers:
  - name: keyserver
  - name: storeserver
  - name: dirserver
    flags:
      kind: server
domain: example.com
`

const (
	ann   = upspin.UserName("ann@example.com")
	chris = upspin.UserName("chris@example.com")
	kelly = upspin.UserName("kelly@example.com")
	lee   = upspin.UserName("lee@example.com")
)

// devNull gives EOF on read and absorbs anything error-free on write, like Unix's /dev/null.
type devNull struct{}

func (devNull) Write(b []byte) (int, error) { return len(b), nil }
func (devNull) Read([]byte) (int, error)    { return 0, io.EOF }
func (devNull) Close() error                { return nil }

// runner controls the execution of a sequence of cmdTests.
// It holds state, including the running upbox instance, and
// as the cmdTests are run the state of the upbox and its servers
// are modified and available to subsequent subcommands.
// It's a little bit like the upspin shell command, but through
// upbox can start the test services and provides mechanisms
// to valid results and test state.
type runner struct {
	// fs, not flag.CommandLine, holds the flags for the upspin state.
	fs *flag.FlagSet
	// state is the the internal state of the upspin command.
	state *State
	// schema holds the running upbox instance.
	schema *upbox.Schema
	// failed is set to true when a command fails; following subcommands are ignored.
	// It is reset before the next cmdTest runs.
	failed bool
}

// runOne runs a single subcommand.
func (r *runner) runOne(t *testing.T, cmdLine string) {
	if r.failed {
		return
	}
	// If the command calls Exit or Exitf, that will panic.
	// It may be benign; if not, the reason is in standard error.
	// We catch the panic here, which is sufficient to capture the error output.
	defer func() {
		rec := recover()
		switch problem := rec.(type) {
		case nil:
		case string:
			if problem == "exit" {
				// OK; this was a subcommand calling exit
				return
			}
			r.failed = true
			t.Errorf("%v", problem)
		default:
			t.Errorf("%v", problem)
		}
	}()
	r.state.run(strings.Fields(cmdLine))
}

// run runs all the subcommands in cmd.
func (r *runner) run(cmd *cmdTest) func(t *testing.T) {
	return func(t *testing.T) {
		stdout := new(bytes.Buffer)
		stderr := new(bytes.Buffer)
		var stdin io.ReadCloser = devNull{}
		if cmd.stdin != "" {
			stdin = ioutil.NopCloser(strings.NewReader(cmd.stdin))
		}
		r.state.SetIO(stdin, stdout, stderr)
		defer r.state.DefaultIO()
		r.state.Interactive = true // So we can regain control after an error.
		for _, cmdLine := range cmd.cmds {
			r.runOne(t, cmdLine)
		}
		cmd.post(t, r, cmd, stdout.String(), stderr.String())
	}
}

// config returns the file name of the config file for the given user.
func (r *runner) config(userName upspin.UserName) string {
	return r.schema.Config(string(userName))
}

// expect is a post function that verifies that standard output from the
// command contains all the words, in order.
func expect(words ...string) func(t *testing.T, r *runner, cmd *cmdTest, stdout, stderr string) {
	return func(t *testing.T, r *runner, cmd *cmdTest, stdout, stderr string) {
		if stderr != "" {
			t.Fatalf("%q: unexpected error:\n\t%q", cmd.name, stderr)
		}
		// Stdout should contain all words, in order, non-abutting.
		out := stdout
		prev := "beginning"
		for _, word := range words {
			index := strings.Index(out, word)
			if index < 0 {
				t.Fatalf("%q: output did not contain %q after %q. output:\n%s", cmd.name, word, prev, stdout)
				return
			}
			prev = word
			out = out[index:]
		}
	}
}

// expectNoOutput is a post function that verifies that standard output from the
// command is empty.
func expectNoOutput() func(t *testing.T, r *runner, cmd *cmdTest, stdout, stderr string) {
	return func(t *testing.T, r *runner, cmd *cmdTest, stdout, stderr string) {
		if stderr != "" {
			t.Fatalf("%q: unexpected error:\n\t%q", cmd.name, stderr)
		}
		if stdout != "" {
			t.Fatalf("%q: unexpected output:\n\t%q", cmd.name, stdout)
		}
	}
}

// fail is a post function that verifies that standard error contains the text of errStr.
func fail(errStr string) func(t *testing.T, r *runner, cmd *cmdTest, stdout, stderr string) {
	return func(t *testing.T, r *runner, cmd *cmdTest, stdout, stderr string) {
		if stderr == "" {
			t.Fatalf("%q: expected error, got none", cmd.name)
		}
		if !strings.Contains(stderr, errStr) {
			t.Fatalf("%q: unexpected error (expected %q)\n\t%q", cmd.name, errStr, stderr)
		}
	}
}

// dump is a post function that just prints the stdout and stderr.
// If Continue is false, dump calls t.Fatal.
// The function is handy when debugging cmdTest scripts.
func dump(Continue bool) func(t *testing.T, r *runner, cmd *cmdTest, stdout, stderr string) {
	return func(t *testing.T, r *runner, cmd *cmdTest, stdout, stderr string) {
		t.Errorf("Stdout:\n%s\n", stdout)
		t.Errorf("Stderr:\n%s\n", stderr)
		if !Continue {
			t.Fatal("dump stops test")
		}
	}
}

// do is just a shorthand to make the cmdTests format more neatly.
func do(s ...string) []string {
	return s
}

// putFile is a cmdTest to add the named file with the given contents and
// check that it is created.
func putFile(user upspin.UserName, name, contents string) cmdTest {
	return cmdTest{
		name: fmt.Sprintf("add %s", name),
		user: user,
		cmds: do(
			"put "+name,
			"get "+name,
		),
		stdin: contents,
		post:  expect(contents),
	}
}

// testTempDir creates, if not already present, a temporary directory
// with basename dir. It panics if it does not exist and cannot be created.
func testTempDir(dir string, keepOld bool) string {
	dir = filepath.Join(os.TempDir(), dir)
	if !keepOld {
		if err := os.RemoveAll(dir); err != nil {
			panic(err)
		}
	}
	err := os.Mkdir(dir, 0700)
	if err != nil && !os.IsExist(err) {
		panic(err)
	}
	return dir
}

// testTempGlob calls testTempDir(dir, keepOld) and returns
// its name appended with "/*".
func testTempGlob(dir string) string {
	return filepath.Join(testTempDir(dir, keepOld), "*")
}

// keygenVerify is a post function for keygen itself.
// It verifies that the keys were created correctly,
// and removes the directory if persist is false.
func keygenVerify(dir, public, secret, secret2 string, persist bool) func(t *testing.T, r *runner, cmd *cmdTest, stdout, stderr string) {
	return func(t *testing.T, r *runner, cmd *cmdTest, stdout, stderr string) {
		t.Log("stdout:", stdout)
		t.Log("stderr:", stdout)
		keyVerify(t, filepath.Join(dir, "public.upspinkey"), public)
		keyVerify(t, filepath.Join(dir, "secret.upspinkey"), secret)
		if secret2 != "" {
			keyVerify(t, filepath.Join(dir, "secret2.upspinkey"), secret2)
		}
		if !persist {
			os.RemoveAll(dir)
		}
	}
}

func keyVerify(t *testing.T, name, prefix string) {
	key, err := ioutil.ReadFile(name)
	if err != nil {
		t.Errorf("cannot read key %q: %v", name, err)
	}
	if !strings.Contains(string(key), prefix) {
		if len(key) > 16 {
			key = key[:16]
		}
		t.Errorf("invalid key: got %q...; expected %q...", key, prefix)
	}
}

// suffixedUserExists is a post function. It returns a function that ensures that a
// config file and key files exist for the suffixed user.
func suffixedUserExists(user, suffix string) func(t *testing.T, r *runner, cmd *cmdTest, stdout, stderr string) {
	return func(t *testing.T, r *runner, cmd *cmdTest, stdout, stderr string) {
		// Both config files should exist.
		cfgFile := r.config(upspin.UserName(user + "@example.com"))
		if _, err := os.Stat(cfgFile); err != nil {
			t.Fatalf("%s", err)
		}
		suser := fmt.Sprintf("s+%s@example.com", user, suffix)
		scfgFile := cfgFile + "." + suffix
		if _, err := os.Stat(scfgFile); err != nil {
			t.Fatalf("%s: %s", err)
		}

		// Key files should exist.
		secretsDir := testTempDir("key", keepOld)
		if _, err := os.Stat(filepath.Join(secretsDir, "public.upspinkey")); err != nil {
			t.Fatalf("%s: %s", suser, err)
		}
		if _, err := os.Stat(filepath.Join(secretsDir, "secret.upspinkey")); err != nil {
			t.Fatalf("%s: %s", suser, err)
		}
	}
}
<commit_msg>cmd/upspin: fix some problems reported by 'go vet'<commit_after>// Copyright 2017 The Upspin 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 main

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"testing"

	"upspin.io/test/testutil"
	"upspin.io/upbox"
	"upspin.io/upspin"
)

var allCmdTests = []*[]cmdTest{
	&basicCmdTests,
	&cpTests,
	&globTests,
	&keygenTests,
	&shareTests,
	&suffixedUserTests,
}

// TestCommands runs the tests defined in cmdTests as subtests.
func TestCommands(t *testing.T) {
	// Set up upbox.
	portString, err := testutil.PickPort()
	if err != nil {
		t.Fatal(err)
	}
	port, _ := strconv.Atoi(portString)
	schema, err := upbox.SchemaFromYAML(upboxSchema, port)
	if err != nil {
		t.Fatalf("setting up schema: %v", err)
	}
	err = schema.Start()
	if err != nil {
		t.Fatalf("starting schema: %v", err)
	}

	// Loop over the tests in sequence, building state as we go.
	for _, testSuite := range allCmdTests {
		for _, test := range *testSuite {
			// We create a runner for each cmdTest so the Config and State
			// are constructed from the environment each time.
			r := &runner{
				fs:     flag.NewFlagSet(test.name, flag.PanicOnError), // panic if there's trouble.
				schema: schema,
			}
			state, _, ok := setup(r.fs, []string{"-config=" + r.config(test.user), "test"})
			if !ok {
				t.Fatal("setup failed; bad arg list?")
			}
			r.state = state
			t.Run(test.name, r.run(&test))
		}
	}

	// Tear down upbox.
	schema.Stop()
}

// TODO: Loop over server implementations?

const upboxSchema = `
users:
  - name: ann@example.com
  - name: chris@example.com
  - name: kelly@example.com
  - name: lee@example.com
servers:
  - name: keyserver
  - name: storeserver
  - name: dirserver
    flags:
      kind: server
domain: example.com
`

const (
	ann   = upspin.UserName("ann@example.com")
	chris = upspin.UserName("chris@example.com")
	kelly = upspin.UserName("kelly@example.com")
	lee   = upspin.UserName("lee@example.com")
)

// devNull gives EOF on read and absorbs anything error-free on write, like Unix's /dev/null.
type devNull struct{}

func (devNull) Write(b []byte) (int, error) { return len(b), nil }
func (devNull) Read([]byte) (int, error)    { return 0, io.EOF }
func (devNull) Close() error                { return nil }

// runner controls the execution of a sequence of cmdTests.
// It holds state, including the running upbox instance, and
// as the cmdTests are run the state of the upbox and its servers
// are modified and available to subsequent subcommands.
// It's a little bit like the upspin shell command, but through
// upbox can start the test services and provides mechanisms
// to valid results and test state.
type runner struct {
	// fs, not flag.CommandLine, holds the flags for the upspin state.
	fs *flag.FlagSet
	// state is the the internal state of the upspin command.
	state *State
	// schema holds the running upbox instance.
	schema *upbox.Schema
	// failed is set to true when a command fails; following subcommands are ignored.
	// It is reset before the next cmdTest runs.
	failed bool
}

// runOne runs a single subcommand.
func (r *runner) runOne(t *testing.T, cmdLine string) {
	if r.failed {
		return
	}
	// If the command calls Exit or Exitf, that will panic.
	// It may be benign; if not, the reason is in standard error.
	// We catch the panic here, which is sufficient to capture the error output.
	defer func() {
		rec := recover()
		switch problem := rec.(type) {
		case nil:
		case string:
			if problem == "exit" {
				// OK; this was a subcommand calling exit
				return
			}
			r.failed = true
			t.Errorf("%v", problem)
		default:
			t.Errorf("%v", problem)
		}
	}()
	r.state.run(strings.Fields(cmdLine))
}

// run runs all the subcommands in cmd.
func (r *runner) run(cmd *cmdTest) func(t *testing.T) {
	return func(t *testing.T) {
		stdout := new(bytes.Buffer)
		stderr := new(bytes.Buffer)
		var stdin io.ReadCloser = devNull{}
		if cmd.stdin != "" {
			stdin = ioutil.NopCloser(strings.NewReader(cmd.stdin))
		}
		r.state.SetIO(stdin, stdout, stderr)
		defer r.state.DefaultIO()
		r.state.Interactive = true // So we can regain control after an error.
		for _, cmdLine := range cmd.cmds {
			r.runOne(t, cmdLine)
		}
		cmd.post(t, r, cmd, stdout.String(), stderr.String())
	}
}

// config returns the file name of the config file for the given user.
func (r *runner) config(userName upspin.UserName) string {
	return r.schema.Config(string(userName))
}

// expect is a post function that verifies that standard output from the
// command contains all the words, in order.
func expect(words ...string) func(t *testing.T, r *runner, cmd *cmdTest, stdout, stderr string) {
	return func(t *testing.T, r *runner, cmd *cmdTest, stdout, stderr string) {
		if stderr != "" {
			t.Fatalf("%q: unexpected error:\n\t%q", cmd.name, stderr)
		}
		// Stdout should contain all words, in order, non-abutting.
		out := stdout
		prev := "beginning"
		for _, word := range words {
			index := strings.Index(out, word)
			if index < 0 {
				t.Fatalf("%q: output did not contain %q after %q. output:\n%s", cmd.name, word, prev, stdout)
				return
			}
			prev = word
			out = out[index:]
		}
	}
}

// expectNoOutput is a post function that verifies that standard output from the
// command is empty.
func expectNoOutput() func(t *testing.T, r *runner, cmd *cmdTest, stdout, stderr string) {
	return func(t *testing.T, r *runner, cmd *cmdTest, stdout, stderr string) {
		if stderr != "" {
			t.Fatalf("%q: unexpected error:\n\t%q", cmd.name, stderr)
		}
		if stdout != "" {
			t.Fatalf("%q: unexpected output:\n\t%q", cmd.name, stdout)
		}
	}
}

// fail is a post function that verifies that standard error contains the text of errStr.
func fail(errStr string) func(t *testing.T, r *runner, cmd *cmdTest, stdout, stderr string) {
	return func(t *testing.T, r *runner, cmd *cmdTest, stdout, stderr string) {
		if stderr == "" {
			t.Fatalf("%q: expected error, got none", cmd.name)
		}
		if !strings.Contains(stderr, errStr) {
			t.Fatalf("%q: unexpected error (expected %q)\n\t%q", cmd.name, errStr, stderr)
		}
	}
}

// dump is a post function that just prints the stdout and stderr.
// If Continue is false, dump calls t.Fatal.
// The function is handy when debugging cmdTest scripts.
func dump(Continue bool) func(t *testing.T, r *runner, cmd *cmdTest, stdout, stderr string) {
	return func(t *testing.T, r *runner, cmd *cmdTest, stdout, stderr string) {
		t.Errorf("Stdout:\n%s\n", stdout)
		t.Errorf("Stderr:\n%s\n", stderr)
		if !Continue {
			t.Fatal("dump stops test")
		}
	}
}

// do is just a shorthand to make the cmdTests format more neatly.
func do(s ...string) []string {
	return s
}

// putFile is a cmdTest to add the named file with the given contents and
// check that it is created.
func putFile(user upspin.UserName, name, contents string) cmdTest {
	return cmdTest{
		name: fmt.Sprintf("add %s", name),
		user: user,
		cmds: do(
			"put "+name,
			"get "+name,
		),
		stdin: contents,
		post:  expect(contents),
	}
}

// testTempDir creates, if not already present, a temporary directory
// with basename dir. It panics if it does not exist and cannot be created.
func testTempDir(dir string, keepOld bool) string {
	dir = filepath.Join(os.TempDir(), dir)
	if !keepOld {
		if err := os.RemoveAll(dir); err != nil {
			panic(err)
		}
	}
	err := os.Mkdir(dir, 0700)
	if err != nil && !os.IsExist(err) {
		panic(err)
	}
	return dir
}

// testTempGlob calls testTempDir(dir, keepOld) and returns
// its name appended with "/*".
func testTempGlob(dir string) string {
	return filepath.Join(testTempDir(dir, keepOld), "*")
}

// keygenVerify is a post function for keygen itself.
// It verifies that the keys were created correctly,
// and removes the directory if persist is false.
func keygenVerify(dir, public, secret, secret2 string, persist bool) func(t *testing.T, r *runner, cmd *cmdTest, stdout, stderr string) {
	return func(t *testing.T, r *runner, cmd *cmdTest, stdout, stderr string) {
		t.Log("stdout:", stdout)
		t.Log("stderr:", stdout)
		keyVerify(t, filepath.Join(dir, "public.upspinkey"), public)
		keyVerify(t, filepath.Join(dir, "secret.upspinkey"), secret)
		if secret2 != "" {
			keyVerify(t, filepath.Join(dir, "secret2.upspinkey"), secret2)
		}
		if !persist {
			os.RemoveAll(dir)
		}
	}
}

func keyVerify(t *testing.T, name, prefix string) {
	key, err := ioutil.ReadFile(name)
	if err != nil {
		t.Errorf("cannot read key %q: %v", name, err)
	}
	if !strings.Contains(string(key), prefix) {
		if len(key) > 16 {
			key = key[:16]
		}
		t.Errorf("invalid key: got %q...; expected %q...", key, prefix)
	}
}

// suffixedUserExists is a post function. It returns a function that ensures that a
// config file and key files exist for the suffixed user.
func suffixedUserExists(user, suffix string) func(t *testing.T, r *runner, cmd *cmdTest, stdout, stderr string) {
	return func(t *testing.T, r *runner, cmd *cmdTest, stdout, stderr string) {
		// Both config files should exist.
		cfgFile := r.config(upspin.UserName(user + "@example.com"))
		if _, err := os.Stat(cfgFile); err != nil {
			t.Fatalf("%s", err)
		}
		suser := fmt.Sprintf("%s+%s@example.com", user, suffix)
		scfgFile := cfgFile + "." + suffix
		if _, err := os.Stat(scfgFile); err != nil {
			t.Fatalf("%s: %s", suser, err)
		}

		// Key files should exist.
		secretsDir := testTempDir("key", keepOld)
		if _, err := os.Stat(filepath.Join(secretsDir, "public.upspinkey")); err != nil {
			t.Fatalf("%s: %s", suser, err)
		}
		if _, err := os.Stat(filepath.Join(secretsDir, "secret.upspinkey")); err != nil {
			t.Fatalf("%s: %s", suser, err)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"sync"
	"time"

	"github.com/google/subcommands"
	"github.com/levinalex/orthanctool/api"
	"github.com/levinalex/orthanctool/patientheap"
)

const patientDetailPageSize = 200
const reverseChangeIteratorChunkSize = 1000

type recentPatientsCommand struct {
	cmdArgs             []string
	orthanc             apiFlag
	pollFutureChanges   bool
	pollIntervalSeconds int
}

func RecentPatientsCommand() *recentPatientsCommand {
	return &recentPatientsCommand{}
}

func (c *recentPatientsCommand) Name() string { return "recent-patients" }
func (c *recentPatientsCommand) Usage() string {
	return c.Name() + ` --orthanc orthanc_url [command...]:
	Iterates over all patients stored in Orthanc roughly in most recently changed order.
	Outputs JSON with patient ID and LastUpdate timestamp.
	If <command> is given, it will be run for each patient and JSON will be passed to it via stdin.` + "\n"
}
func (c *recentPatientsCommand) Synopsis() string {
	return "yield patient details for most recently changed patients"
}

func (c *recentPatientsCommand) SetFlags(f *flag.FlagSet) {
	f.Var(&c.orthanc, "orthanc", "Orthanc URL")
	f.IntVar(&c.pollIntervalSeconds, "poll-interval", 60, "poll interval in seconds")
	f.BoolVar(&c.pollFutureChanges, "poll", true, "continuously poll for changes")
}

func (c *recentPatientsCommand) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
	if c.orthanc.Api == nil {
		return fail(fmt.Errorf("orthanc URL not set"))
	}

	c.cmdArgs = f.Args()[0:]

	err := c.run(ctx, c.orthanc.Api)
	if err != nil {
		return fail(err)
	}

	return subcommands.ExitSuccess
}

// patientDetails iterates over all existing patients.
func patientDetails(done <-chan struct{}, wg *sync.WaitGroup, source *api.Api, patients chan<- patientheap.Patient, e ErrorFunc) {
	defer wg.Done()

	index := 0
	for {
		details, err := source.PatientDetailsSince(index, patientDetailPageSize)
		if err != nil {
			e(err)
			return
		}
		if len(details) == 0 {
			return
		}
		index += len(details)

		for _, d := range details {
			select {
			case patients <- patientheap.Patient{ID: d.ID, LastUpdate: d.LastUpdate}:
			case <-done:
				return
			}
		}
	}
}
func watchForChanges(ctx context.Context, startIndex, stopIndex int, source *api.Api, patients chan<- patientheap.Patient, pollInterval time.Duration, e ErrorFunc) {
	err := api.ChangeWatch{
		StartIndex: startIndex, StopIndex: stopIndex,
		PollInterval: pollInterval,
	}.
		Run(source, ctx, func(cng api.ChangeResult) {
			if cng.ChangeType == "StablePatient" {
				patients <- patientheap.Patient{ID: cng.ID, LastUpdate: cng.Date}
			}
		})

	if err != nil {
		e(err)
	}
}

func (c *recentPatientsCommand) cmdAction(pat patientheap.PatientOutput) error {
	cmd := c.cmdArgs

	b, err := json.Marshal(pat)
	if err != nil {
		return err
	}
	if len(cmd) == 0 {
		fmt.Println(string(b))
	} else {
		cmd := exec.Command(cmd[0], cmd[1:]...)
		cmd.Stdin = bytes.NewBuffer(b)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		err = cmd.Run()
		if err != nil {
			return err
		}
	}
	return nil
}

func (c *recentPatientsCommand) run(ctx context.Context, source *api.Api) error {
	var err error
	wg, wg2 := sync.WaitGroup{}, sync.WaitGroup{}
	ctx, cancelFunc := context.WithCancel(ctx)
	e := func(e error) {
		cancelFunc()
		err = e
	}

	patients := make(chan patientheap.Patient, 0)
	sortedPatients := patientheap.SortPatients(ctx.Done(), patients)

	wg.Add(1)
	go patientDetails(ctx.Done(), &wg, source, patients, e)

	wg.Add(1)
	go func() {
		defer wg.Done()

		_, lastIndex, err := source.LastChange()
		if err != nil {
			e(err)
			return
		}

		if c.pollFutureChanges {
			wg.Add(1)
			go func() {
				defer wg.Done()
				watchForChanges(ctx, lastIndex, -1, source, patients,
					time.Duration(c.pollIntervalSeconds)*time.Second, e)
			}()
		}

		to := lastIndex
		for to > 0 {
			from := to - reverseChangeIteratorChunkSize
			watchForChanges(ctx, from, to, source, patients, 0, e) // all past changes up to now
			to = from
		}
	}()

	wg2.Add(1)
	go func() {
		defer wg2.Done()

		for pat := range sortedPatients {
			err := c.cmdAction(pat)
			if err != nil {
				e(err)
				break
			}
		}
	}()

	wg.Wait()

	close(patients)

	wg2.Wait()

	return err
}
<commit_msg>reword help output<commit_after>package main

import (
	"bytes"
	"context"
	"encoding/json"
	"flag"
	"fmt"
	"os"
	"os/exec"
	"sync"
	"time"

	"github.com/google/subcommands"
	"github.com/levinalex/orthanctool/api"
	"github.com/levinalex/orthanctool/patientheap"
)

const patientDetailPageSize = 200
const reverseChangeIteratorChunkSize = 1000

type recentPatientsCommand struct {
	cmdArgs             []string
	orthanc             apiFlag
	pollFutureChanges   bool
	pollIntervalSeconds int
}

func RecentPatientsCommand() *recentPatientsCommand {
	return &recentPatientsCommand{}
}

func (c *recentPatientsCommand) Name() string { return "recent-patients" }
func (c *recentPatientsCommand) Usage() string {
	return c.Name() + ` --orthanc <url> [command...]:
	Iterates over all patients stored in Orthanc roughly in most recently changed order.
	Outputs JSON with patient ID and LastUpdate timestamp.
	If <command> is given, it will be run for each patient and JSON will be passed to it via stdin.` + "\n"
}
func (c *recentPatientsCommand) Synopsis() string {
	return "yield patient details for most recently changed patients"
}

func (c *recentPatientsCommand) SetFlags(f *flag.FlagSet) {
	f.Var(&c.orthanc, "orthanc", "Orthanc URL")
	f.IntVar(&c.pollIntervalSeconds, "poll-interval", 60, "poll interval in seconds")
	f.BoolVar(&c.pollFutureChanges, "poll", true, "continuously poll for changes")
}

func (c *recentPatientsCommand) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
	if c.orthanc.Api == nil {
		return fail(fmt.Errorf("orthanc URL not set"))
	}

	c.cmdArgs = f.Args()[0:]

	err := c.run(ctx, c.orthanc.Api)
	if err != nil {
		return fail(err)
	}

	return subcommands.ExitSuccess
}

// patientDetails iterates over all existing patients.
func patientDetails(done <-chan struct{}, wg *sync.WaitGroup, source *api.Api, patients chan<- patientheap.Patient, e ErrorFunc) {
	defer wg.Done()

	index := 0
	for {
		details, err := source.PatientDetailsSince(index, patientDetailPageSize)
		if err != nil {
			e(err)
			return
		}
		if len(details) == 0 {
			return
		}
		index += len(details)

		for _, d := range details {
			select {
			case patients <- patientheap.Patient{ID: d.ID, LastUpdate: d.LastUpdate}:
			case <-done:
				return
			}
		}
	}
}
func watchForChanges(ctx context.Context, startIndex, stopIndex int, source *api.Api, patients chan<- patientheap.Patient, pollInterval time.Duration, e ErrorFunc) {
	err := api.ChangeWatch{
		StartIndex: startIndex, StopIndex: stopIndex,
		PollInterval: pollInterval,
	}.
		Run(source, ctx, func(cng api.ChangeResult) {
			if cng.ChangeType == "StablePatient" {
				patients <- patientheap.Patient{ID: cng.ID, LastUpdate: cng.Date}
			}
		})

	if err != nil {
		e(err)
	}
}

func (c *recentPatientsCommand) cmdAction(pat patientheap.PatientOutput) error {
	cmd := c.cmdArgs

	b, err := json.Marshal(pat)
	if err != nil {
		return err
	}
	if len(cmd) == 0 {
		fmt.Println(string(b))
	} else {
		cmd := exec.Command(cmd[0], cmd[1:]...)
		cmd.Stdin = bytes.NewBuffer(b)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		err = cmd.Run()
		if err != nil {
			return err
		}
	}
	return nil
}

func (c *recentPatientsCommand) run(ctx context.Context, source *api.Api) error {
	var err error
	wg, wg2 := sync.WaitGroup{}, sync.WaitGroup{}
	ctx, cancelFunc := context.WithCancel(ctx)
	e := func(e error) {
		cancelFunc()
		err = e
	}

	patients := make(chan patientheap.Patient, 0)
	sortedPatients := patientheap.SortPatients(ctx.Done(), patients)

	wg.Add(1)
	go patientDetails(ctx.Done(), &wg, source, patients, e)

	wg.Add(1)
	go func() {
		defer wg.Done()

		_, lastIndex, err := source.LastChange()
		if err != nil {
			e(err)
			return
		}

		if c.pollFutureChanges {
			wg.Add(1)
			go func() {
				defer wg.Done()
				watchForChanges(ctx, lastIndex, -1, source, patients,
					time.Duration(c.pollIntervalSeconds)*time.Second, e)
			}()
		}

		to := lastIndex
		for to > 0 {
			from := to - reverseChangeIteratorChunkSize
			watchForChanges(ctx, from, to, source, patients, 0, e) // all past changes up to now
			to = from
		}
	}()

	wg2.Add(1)
	go func() {
		defer wg2.Done()

		for pat := range sortedPatients {
			err := c.cmdAction(pat)
			if err != nil {
				e(err)
				break
			}
		}
	}()

	wg.Wait()

	close(patients)

	wg2.Wait()

	return err
}
<|endoftext|>


<commit_before>package isolated

import (
	"code.cloudfoundry.org/cli/integration/helpers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
)

var _ = Describe("orgs command", func() {
	Describe("help", func() {
		When("--help flag is set", func() {
			It("displays command usage to output", func() {
				session := helpers.CF("orgs", "--help")
				Eventually(session).Should(Say("NAME:"))
				Eventually(session).Should(Say(`\s+orgs - List all orgs`))
				Eventually(session).Should(Say("USAGE:"))
				Eventually(session).Should(Say(`\s+cf orgs`))
				Eventually(session).Should(Say("ALIAS:"))
				Eventually(session).Should(Say(`\s+o`))
				Eventually(session).Should(Say(`SEE ALSO:`))
				Eventually(session).Should(Say(`create-org, org, org-users`))
				Eventually(session).Should(Exit(0))
			})
		})
	})

	When("the environment is not setup correctly", func() {
		It("fails with the appropriate errors", func() {
			helpers.CheckEnvironmentTargetedCorrectly(false, false, ReadOnlyOrg, "orgs")
		})
	})

	When("the environment is setup correctly", func() {
		var username string

		BeforeEach(func() {
			username = helpers.LoginCF()
		})

		When("there are multiple orgs", func() {
			var orgName1, orgName2, orgName3, orgName4, orgName5 string

			BeforeEach(func() {
				orgName1 = helpers.PrefixedRandomName("INTEGRATION-ORG-XYZ")
				orgName2 = helpers.PrefixedRandomName("INTEGRATION-ORG-456")
				orgName3 = helpers.PrefixedRandomName("INTEGRATION-ORG-ABC")
				orgName4 = helpers.PrefixedRandomName("INTEGRATION-ORG-123")
				orgName5 = helpers.PrefixedRandomName("INTEGRATION-ORG-ghi")
				helpers.CreateOrg(orgName1)
				helpers.CreateOrg(orgName2)
				helpers.CreateOrg(orgName3)
				helpers.CreateOrg(orgName4)
				helpers.CreateOrg(orgName5)
			})

			AfterEach(func() {
				helpers.QuickDeleteOrg(orgName1)
				helpers.QuickDeleteOrg(orgName2)
				helpers.QuickDeleteOrg(orgName3)
				helpers.QuickDeleteOrg(orgName4)
				helpers.QuickDeleteOrg(orgName5)
			})

			It("displays a list of all orgs", func() {
				session := helpers.CF("orgs")
				Eventually(session).Should(Say(`Getting orgs as %s\.\.\.`, username))
				Eventually(session).Should(Say(""))
				Eventually(session).Should(Say("name"))
				Eventually(session).Should(Say("%s", orgName4))
				Eventually(session).Should(Say("%s", orgName2))
				Eventually(session).Should(Say("%s", orgName3))
				Eventually(session).Should(Say("%s", orgName5))
				Eventually(session).Should(Say("%s", orgName1))
				Eventually(session).Should(Exit(0))
			})

			When("the --labels flag is given", func() {

				BeforeEach(func() {
					Eventually(helpers.CF("set-label", "org", orgName1, "environment=production", "tier=backend")).Should(Exit(0))
					Eventually(helpers.CF("set-label", "org", orgName2, "environment=staging", "tier=frontend")).Should(Exit(0))
				})

				It("displays only the organizations with labels that match the expression", func() {
					session := helpers.CF("orgs", "--labels", "environment in (production,staging),tier in (backend)")
					Eventually(session).Should(Exit(0))
					Expect(session).ShouldNot(Say(orgName2))
					Expect(session).Should(Say(orgName1))
				})

				When("the --labels selector is malformed", func() {
					It("errors", func() {
						session := helpers.CF("orgs", "--labels", "malformed in (")
						Eventually(session).Should(Exit(1))
					})
				})
			})
		})

	})
})
<commit_msg>Orgs not needed for malformed selector<commit_after>package isolated

import (
	"code.cloudfoundry.org/cli/integration/helpers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
)

var _ = Describe("orgs command", func() {
	Describe("help", func() {
		When("--help flag is set", func() {
			It("displays command usage to output", func() {
				session := helpers.CF("orgs", "--help")
				Eventually(session).Should(Say("NAME:"))
				Eventually(session).Should(Say(`\s+orgs - List all orgs`))
				Eventually(session).Should(Say("USAGE:"))
				Eventually(session).Should(Say(`\s+cf orgs`))
				Eventually(session).Should(Say("ALIAS:"))
				Eventually(session).Should(Say(`\s+o`))
				Eventually(session).Should(Say(`SEE ALSO:`))
				Eventually(session).Should(Say(`create-org, org, org-users`))
				Eventually(session).Should(Exit(0))
			})
		})
	})

	When("the environment is not setup correctly", func() {
		It("fails with the appropriate errors", func() {
			helpers.CheckEnvironmentTargetedCorrectly(false, false, ReadOnlyOrg, "orgs")
		})
	})

	When("the environment is setup correctly", func() {
		var username string

		BeforeEach(func() {
			username = helpers.LoginCF()
		})

		When("there are multiple orgs", func() {
			var orgName1, orgName2, orgName3, orgName4, orgName5 string

			BeforeEach(func() {
				orgName1 = helpers.PrefixedRandomName("INTEGRATION-ORG-XYZ")
				orgName2 = helpers.PrefixedRandomName("INTEGRATION-ORG-456")
				orgName3 = helpers.PrefixedRandomName("INTEGRATION-ORG-ABC")
				orgName4 = helpers.PrefixedRandomName("INTEGRATION-ORG-123")
				orgName5 = helpers.PrefixedRandomName("INTEGRATION-ORG-ghi")
				helpers.CreateOrg(orgName1)
				helpers.CreateOrg(orgName2)
				helpers.CreateOrg(orgName3)
				helpers.CreateOrg(orgName4)
				helpers.CreateOrg(orgName5)
			})

			AfterEach(func() {
				helpers.QuickDeleteOrg(orgName1)
				helpers.QuickDeleteOrg(orgName2)
				helpers.QuickDeleteOrg(orgName3)
				helpers.QuickDeleteOrg(orgName4)
				helpers.QuickDeleteOrg(orgName5)
			})

			It("displays a list of all orgs", func() {
				session := helpers.CF("orgs")
				Eventually(session).Should(Say(`Getting orgs as %s\.\.\.`, username))
				Eventually(session).Should(Say(""))
				Eventually(session).Should(Say("name"))
				Eventually(session).Should(Say("%s", orgName4))
				Eventually(session).Should(Say("%s", orgName2))
				Eventually(session).Should(Say("%s", orgName3))
				Eventually(session).Should(Say("%s", orgName5))
				Eventually(session).Should(Say("%s", orgName1))
				Eventually(session).Should(Exit(0))
			})

			When("the --labels flag is given", func() {
				When("the --labels selector is malformed", func() {
					It("errors", func() {
						session := helpers.CF("orgs", "--labels", "malformed in (")
						Eventually(session).Should(Exit(1))
					})
				})

				When("there are labels on an org", func() {
					BeforeEach(func() {
						Eventually(helpers.CF("set-label", "org", orgName1, "environment=production", "tier=backend")).Should(Exit(0))
						Eventually(helpers.CF("set-label", "org", orgName2, "environment=staging", "tier=frontend")).Should(Exit(0))
					})

					It("displays only the organizations with labels that match the expression", func() {
						session := helpers.CF("orgs", "--labels", "environment in (production,staging),tier in (backend)")
						Eventually(session).Should(Exit(0))
						Expect(session).ShouldNot(Say(orgName2))
						Expect(session).Should(Say(orgName1))
					})
				})
			})
		})
	})
})
<|endoftext|>