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

// +build race

package atomic

import (
	"runtime"
	"unsafe"
)

// We use runtime.RaceRead() inside of atomic operations to catch races
// between atomic and non-atomic operations.  It will also catch races
// between Mutex.Lock() and mutex overwrite (mu = Mutex{}).  Since we use
// only RaceRead() we won't catch races with non-atomic loads.
// Otherwise (if we use RaceWrite()) we will report races
// between atomic operations (false positives).

var mtx uint32 = 1 // same for all

func CompareAndSwapInt32(val *int32, old, new int32) bool {
	return CompareAndSwapUint32((*uint32)(unsafe.Pointer(val)), uint32(old), uint32(new))
}

func CompareAndSwapUint32(val *uint32, old, new uint32) (swapped bool) {
	swapped = false
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(val))
	runtime.RaceAcquire(unsafe.Pointer(val))
	if *val == old {
		*val = new
		swapped = true
		runtime.RaceReleaseMerge(unsafe.Pointer(val))
	}
	runtime.RaceSemrelease(&mtx)
	return
}

func CompareAndSwapInt64(val *int64, old, new int64) bool {
	return CompareAndSwapUint64((*uint64)(unsafe.Pointer(val)), uint64(old), uint64(new))
}

func CompareAndSwapUint64(val *uint64, old, new uint64) (swapped bool) {
	swapped = false
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(val))
	runtime.RaceAcquire(unsafe.Pointer(val))
	if *val == old {
		*val = new
		swapped = true
		runtime.RaceReleaseMerge(unsafe.Pointer(val))
	}
	runtime.RaceSemrelease(&mtx)
	return
}

func CompareAndSwapPointer(val *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool) {
	swapped = false
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(val))
	runtime.RaceAcquire(unsafe.Pointer(val))
	if *val == old {
		*val = new
		swapped = true
		runtime.RaceReleaseMerge(unsafe.Pointer(val))
	}
	runtime.RaceSemrelease(&mtx)
	return
}

func CompareAndSwapUintptr(val *uintptr, old, new uintptr) (swapped bool) {
	swapped = false
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(val))
	runtime.RaceAcquire(unsafe.Pointer(val))
	if *val == old {
		*val = new
		swapped = true
		runtime.RaceReleaseMerge(unsafe.Pointer(val))
	}
	runtime.RaceSemrelease(&mtx)
	return
}

func AddInt32(val *int32, delta int32) int32 {
	return int32(AddUint32((*uint32)(unsafe.Pointer(val)), uint32(delta)))
}

func AddUint32(val *uint32, delta uint32) (new uint32) {
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(val))
	runtime.RaceAcquire(unsafe.Pointer(val))
	*val = *val + delta
	new = *val
	runtime.RaceReleaseMerge(unsafe.Pointer(val))
	runtime.RaceSemrelease(&mtx)

	return
}

func AddInt64(val *int64, delta int64) int64 {
	return int64(AddUint64((*uint64)(unsafe.Pointer(val)), uint64(delta)))
}

func AddUint64(val *uint64, delta uint64) (new uint64) {
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(val))
	runtime.RaceAcquire(unsafe.Pointer(val))
	*val = *val + delta
	new = *val
	runtime.RaceReleaseMerge(unsafe.Pointer(val))
	runtime.RaceSemrelease(&mtx)

	return
}

func AddUintptr(val *uintptr, delta uintptr) (new uintptr) {
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(val))
	runtime.RaceAcquire(unsafe.Pointer(val))
	*val = *val + delta
	new = *val
	runtime.RaceReleaseMerge(unsafe.Pointer(val))
	runtime.RaceSemrelease(&mtx)

	return
}

func LoadInt32(addr *int32) int32 {
	return int32(LoadUint32((*uint32)(unsafe.Pointer(addr))))
}

func LoadUint32(addr *uint32) (val uint32) {
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(addr))
	runtime.RaceAcquire(unsafe.Pointer(addr))
	val = *addr
	runtime.RaceSemrelease(&mtx)
	return
}

func LoadInt64(addr *int64) int64 {
	return int64(LoadUint64((*uint64)(unsafe.Pointer(addr))))
}

func LoadUint64(addr *uint64) (val uint64) {
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(addr))
	runtime.RaceAcquire(unsafe.Pointer(addr))
	val = *addr
	runtime.RaceSemrelease(&mtx)
	return
}

func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer) {
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(addr))
	runtime.RaceAcquire(unsafe.Pointer(addr))
	val = *addr
	runtime.RaceSemrelease(&mtx)
	return
}

func LoadUintptr(addr *uintptr) (val uintptr) {
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(val))
	runtime.RaceAcquire(unsafe.Pointer(addr))
	val = *addr
	runtime.RaceSemrelease(&mtx)
	return
}

func StoreInt32(addr *int32, val int32) {
	StoreUint32((*uint32)(unsafe.Pointer(addr)), uint32(val))
}

func StoreUint32(addr *uint32, val uint32) {
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(addr))
	*addr = val
	runtime.RaceRelease(unsafe.Pointer(addr))
	runtime.RaceSemrelease(&mtx)
}

func StoreInt64(addr *int64, val int64) {
	StoreUint64((*uint64)(unsafe.Pointer(addr)), uint64(val))
}

func StoreUint64(addr *uint64, val uint64) {
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(addr))
	*addr = val
	runtime.RaceRelease(unsafe.Pointer(addr))
	runtime.RaceSemrelease(&mtx)
}

func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer) {
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(val))
	*addr = val
	runtime.RaceRelease(unsafe.Pointer(addr))
	runtime.RaceSemrelease(&mtx)
}

func StoreUintptr(addr *uintptr, val uintptr) {
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(val))
	*addr = val
	runtime.RaceRelease(unsafe.Pointer(addr))
	runtime.RaceSemrelease(&mtx)
}
<commit_msg>sync/atomic: fix race instrumentation<commit_after>// Copyright 2011 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build race

package atomic

import (
	"runtime"
	"unsafe"
)

// We use runtime.RaceRead() inside of atomic operations to catch races
// between atomic and non-atomic operations.  It will also catch races
// between Mutex.Lock() and mutex overwrite (mu = Mutex{}).  Since we use
// only RaceRead() we won't catch races with non-atomic loads.
// Otherwise (if we use RaceWrite()) we will report races
// between atomic operations (false positives).

var mtx uint32 = 1 // same for all

func CompareAndSwapInt32(val *int32, old, new int32) bool {
	return CompareAndSwapUint32((*uint32)(unsafe.Pointer(val)), uint32(old), uint32(new))
}

func CompareAndSwapUint32(val *uint32, old, new uint32) (swapped bool) {
	swapped = false
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(val))
	runtime.RaceAcquire(unsafe.Pointer(val))
	if *val == old {
		*val = new
		swapped = true
		runtime.RaceReleaseMerge(unsafe.Pointer(val))
	}
	runtime.RaceSemrelease(&mtx)
	return
}

func CompareAndSwapInt64(val *int64, old, new int64) bool {
	return CompareAndSwapUint64((*uint64)(unsafe.Pointer(val)), uint64(old), uint64(new))
}

func CompareAndSwapUint64(val *uint64, old, new uint64) (swapped bool) {
	swapped = false
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(val))
	runtime.RaceAcquire(unsafe.Pointer(val))
	if *val == old {
		*val = new
		swapped = true
		runtime.RaceReleaseMerge(unsafe.Pointer(val))
	}
	runtime.RaceSemrelease(&mtx)
	return
}

func CompareAndSwapPointer(val *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool) {
	swapped = false
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(val))
	runtime.RaceAcquire(unsafe.Pointer(val))
	if *val == old {
		*val = new
		swapped = true
		runtime.RaceReleaseMerge(unsafe.Pointer(val))
	}
	runtime.RaceSemrelease(&mtx)
	return
}

func CompareAndSwapUintptr(val *uintptr, old, new uintptr) (swapped bool) {
	swapped = false
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(val))
	runtime.RaceAcquire(unsafe.Pointer(val))
	if *val == old {
		*val = new
		swapped = true
		runtime.RaceReleaseMerge(unsafe.Pointer(val))
	}
	runtime.RaceSemrelease(&mtx)
	return
}

func AddInt32(val *int32, delta int32) int32 {
	return int32(AddUint32((*uint32)(unsafe.Pointer(val)), uint32(delta)))
}

func AddUint32(val *uint32, delta uint32) (new uint32) {
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(val))
	runtime.RaceAcquire(unsafe.Pointer(val))
	*val = *val + delta
	new = *val
	runtime.RaceReleaseMerge(unsafe.Pointer(val))
	runtime.RaceSemrelease(&mtx)

	return
}

func AddInt64(val *int64, delta int64) int64 {
	return int64(AddUint64((*uint64)(unsafe.Pointer(val)), uint64(delta)))
}

func AddUint64(val *uint64, delta uint64) (new uint64) {
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(val))
	runtime.RaceAcquire(unsafe.Pointer(val))
	*val = *val + delta
	new = *val
	runtime.RaceReleaseMerge(unsafe.Pointer(val))
	runtime.RaceSemrelease(&mtx)

	return
}

func AddUintptr(val *uintptr, delta uintptr) (new uintptr) {
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(val))
	runtime.RaceAcquire(unsafe.Pointer(val))
	*val = *val + delta
	new = *val
	runtime.RaceReleaseMerge(unsafe.Pointer(val))
	runtime.RaceSemrelease(&mtx)

	return
}

func LoadInt32(addr *int32) int32 {
	return int32(LoadUint32((*uint32)(unsafe.Pointer(addr))))
}

func LoadUint32(addr *uint32) (val uint32) {
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(addr))
	runtime.RaceAcquire(unsafe.Pointer(addr))
	val = *addr
	runtime.RaceSemrelease(&mtx)
	return
}

func LoadInt64(addr *int64) int64 {
	return int64(LoadUint64((*uint64)(unsafe.Pointer(addr))))
}

func LoadUint64(addr *uint64) (val uint64) {
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(addr))
	runtime.RaceAcquire(unsafe.Pointer(addr))
	val = *addr
	runtime.RaceSemrelease(&mtx)
	return
}

func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer) {
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(addr))
	runtime.RaceAcquire(unsafe.Pointer(addr))
	val = *addr
	runtime.RaceSemrelease(&mtx)
	return
}

func LoadUintptr(addr *uintptr) (val uintptr) {
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(addr))
	runtime.RaceAcquire(unsafe.Pointer(addr))
	val = *addr
	runtime.RaceSemrelease(&mtx)
	return
}

func StoreInt32(addr *int32, val int32) {
	StoreUint32((*uint32)(unsafe.Pointer(addr)), uint32(val))
}

func StoreUint32(addr *uint32, val uint32) {
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(addr))
	*addr = val
	runtime.RaceRelease(unsafe.Pointer(addr))
	runtime.RaceSemrelease(&mtx)
}

func StoreInt64(addr *int64, val int64) {
	StoreUint64((*uint64)(unsafe.Pointer(addr)), uint64(val))
}

func StoreUint64(addr *uint64, val uint64) {
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(addr))
	*addr = val
	runtime.RaceRelease(unsafe.Pointer(addr))
	runtime.RaceSemrelease(&mtx)
}

func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer) {
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(addr))
	*addr = val
	runtime.RaceRelease(unsafe.Pointer(addr))
	runtime.RaceSemrelease(&mtx)
}

func StoreUintptr(addr *uintptr, val uintptr) {
	runtime.RaceSemacquire(&mtx)
	runtime.RaceRead(unsafe.Pointer(addr))
	*addr = val
	runtime.RaceRelease(unsafe.Pointer(addr))
	runtime.RaceSemrelease(&mtx)
}
<|endoftext|>


<commit_before>package write

import (
	"bufio"
	"bytes"
	"context"
	"fmt"
	"io"
	"time"

	"github.com/influxdata/platform"
)

const (
	// DefaultMaxBytes is 500KB; this is typically 250 to 500 lines.
	DefaultMaxBytes = 500000
	// DefaultInterval will flush every 10 seconds.
	DefaultInterval = 10 * time.Second
)

// batcher is a write service that batches for another write service.
var _ platform.WriteService = (*Batcher)(nil)

// Batcher batches line protocol for sends to output.
type Batcher struct {
	MaxFlushBytes    int                   // MaxFlushBytes is the maximum number of bytes to buffer before flushing
	MaxFlushInterval time.Duration         // MaxFlushInterval is the maximum amount of time to wait before flushing
	Service          platform.WriteService // Service receives batches flushed from Batcher.
}

// Write reads r in batches and sends to the output.
func (b *Batcher) Write(ctx context.Context, org, bucket platform.ID, r io.Reader) error {
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	if b.Service == nil {
		return fmt.Errorf("destination write service required")
	}

	lines := make(chan []byte)

	writeErrC := make(chan error)
	go b.write(ctx, org, bucket, lines, writeErrC)

	readErrC := make(chan error)
	go b.read(ctx, r, lines, readErrC)

	// loop is needed in the case that the read finishes without an
	// error, but, the write has yet to complete.
	for {
		select {
		case <-ctx.Done():
			return ctx.Err()
		case err := <-readErrC:
			// only exit if the read has an error
			// read will have closed the lines channel signaling write to exit
			if err != nil {
				return err
			}
		case err := <-writeErrC:
			// if write finishes, exit immediately. reads may block forever
			return err
		}
	}
}

// read will close the line channel when there is no more data, or an error occurs.
// it is possible for an io.Reader to block forever; Write's context can be
// used to cancel, but, it's possible there will be dangling read go routines.
func (b *Batcher) read(ctx context.Context, r io.Reader, lines chan<- []byte, errC chan<- error) {
	scanner := bufio.NewScanner(r)
	scanner.Split(ScanLines)
	for scanner.Scan() {
		// exit early if the context is done
		select {
		case lines <- scanner.Bytes():
		case <-ctx.Done():
			close(lines)
			errC <- ctx.Err()
			return
		}
	}
	close(lines)
	errC <- scanner.Err()
}

// finishes when the lines channel is closed or context is done.
// if an error occurs while writing data to the write service, the error is send in the
// errC channel and the function returns.
func (b *Batcher) write(ctx context.Context, org, bucket platform.ID, lines <-chan []byte, errC chan<- error) {
	flushInterval := b.MaxFlushInterval
	if flushInterval == 0 {
		flushInterval = DefaultInterval
	}

	maxBytes := b.MaxFlushBytes
	if maxBytes == 0 {
		maxBytes = DefaultMaxBytes
	}

	buf := make([]byte, 0, maxBytes)
	r := bytes.NewReader(buf)
	var line []byte
	var more = true

	// if read closes the channel normally, exit the loop
	for more {
		select {
		case line, more = <-lines:
			if more {
				buf = append(buf, line...)
			}
			// write if we exceed the max lines OR read routine has finished
			if len(buf) >= maxBytes || (!more && len(buf) > 0) {
				r.Reset(buf)
				if err := b.Service.Write(ctx, org, bucket, r); err != nil {
					errC <- err
					return
				}
				buf = buf[:0]
			}
		case <-time.After(flushInterval):
			// TODO: worry about timer garbage collection
			if len(buf) > 0 {
				r.Reset(buf)
				if err := b.Service.Write(ctx, org, bucket, r); err != nil {
					errC <- err
					return
				}
				buf = buf[:0]
			}
		case <-ctx.Done():
			errC <- ctx.Err()
			return
		}
	}

	errC <- nil
}

// ScanLines is used in bufio.Scanner.Split to split lines of line protocol.
func ScanLines(data []byte, atEOF bool) (advance int, token []byte, err error) {
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}

	if i := bytes.IndexByte(data, '\n'); i >= 0 {
		// We have a full newline-terminated line.
		return i + 1, data[0 : i+1], nil

	}

	// If we're at EOF, we have a final, non-terminated line. Return it.
	if atEOF {
		return len(data), data, nil
	}

	// Request more data.
	return 0, nil, nil
}
<commit_msg>refactor(write): reuse timers<commit_after>package write

import (
	"bufio"
	"bytes"
	"context"
	"fmt"
	"io"
	"time"

	"github.com/influxdata/platform"
)

const (
	// DefaultMaxBytes is 500KB; this is typically 250 to 500 lines.
	DefaultMaxBytes = 500000
	// DefaultInterval will flush every 10 seconds.
	DefaultInterval = 10 * time.Second
)

// batcher is a write service that batches for another write service.
var _ platform.WriteService = (*Batcher)(nil)

// Batcher batches line protocol for sends to output.
type Batcher struct {
	MaxFlushBytes    int                   // MaxFlushBytes is the maximum number of bytes to buffer before flushing
	MaxFlushInterval time.Duration         // MaxFlushInterval is the maximum amount of time to wait before flushing
	Service          platform.WriteService // Service receives batches flushed from Batcher.
}

// Write reads r in batches and sends to the output.
func (b *Batcher) Write(ctx context.Context, org, bucket platform.ID, r io.Reader) error {
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	if b.Service == nil {
		return fmt.Errorf("destination write service required")
	}

	lines := make(chan []byte)

	writeErrC := make(chan error)
	go b.write(ctx, org, bucket, lines, writeErrC)

	readErrC := make(chan error)
	go b.read(ctx, r, lines, readErrC)

	// loop is needed in the case that the read finishes without an
	// error, but, the write has yet to complete.
	for {
		select {
		case <-ctx.Done():
			return ctx.Err()
		case err := <-readErrC:
			// only exit if the read has an error
			// read will have closed the lines channel signaling write to exit
			if err != nil {
				return err
			}
		case err := <-writeErrC:
			// if write finishes, exit immediately. reads may block forever
			return err
		}
	}
}

// read will close the line channel when there is no more data, or an error occurs.
// it is possible for an io.Reader to block forever; Write's context can be
// used to cancel, but, it's possible there will be dangling read go routines.
func (b *Batcher) read(ctx context.Context, r io.Reader, lines chan<- []byte, errC chan<- error) {
	scanner := bufio.NewScanner(r)
	scanner.Split(ScanLines)
	for scanner.Scan() {
		// exit early if the context is done
		select {
		case lines <- scanner.Bytes():
		case <-ctx.Done():
			close(lines)
			errC <- ctx.Err()
			return
		}
	}
	close(lines)
	errC <- scanner.Err()
}

// finishes when the lines channel is closed or context is done.
// if an error occurs while writing data to the write service, the error is send in the
// errC channel and the function returns.
func (b *Batcher) write(ctx context.Context, org, bucket platform.ID, lines <-chan []byte, errC chan<- error) {
	flushInterval := b.MaxFlushInterval
	if flushInterval == 0 {
		flushInterval = DefaultInterval
	}

	maxBytes := b.MaxFlushBytes
	if maxBytes == 0 {
		maxBytes = DefaultMaxBytes
	}

	timer := time.NewTimer(flushInterval)
	defer func() { _ = timer.Stop() }()

	buf := make([]byte, 0, maxBytes)
	r := bytes.NewReader(buf)

	var line []byte
	var more = true
	// if read closes the channel normally, exit the loop
	for more {
		select {
		case line, more = <-lines:
			if more {
				buf = append(buf, line...)
			}
			// write if we exceed the max lines OR read routine has finished
			if len(buf) >= maxBytes || (!more && len(buf) > 0) {
				r.Reset(buf)
				timer.Reset(flushInterval)
				if err := b.Service.Write(ctx, org, bucket, r); err != nil {
					errC <- err
					return
				}
				buf = buf[:0]
			}
		case <-timer.C:
			if len(buf) > 0 {
				r.Reset(buf)
				timer.Reset(flushInterval)
				if err := b.Service.Write(ctx, org, bucket, r); err != nil {
					errC <- err
					return
				}
				buf = buf[:0]
			}
		case <-ctx.Done():
			errC <- ctx.Err()
			return
		}
	}

	errC <- nil
}

// ScanLines is used in bufio.Scanner.Split to split lines of line protocol.
func ScanLines(data []byte, atEOF bool) (advance int, token []byte, err error) {
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}

	if i := bytes.IndexByte(data, '\n'); i >= 0 {
		// We have a full newline-terminated line.
		return i + 1, data[0 : i+1], nil

	}

	// If we're at EOF, we have a final, non-terminated line. Return it.
	if atEOF {
		return len(data), data, nil
	}

	// Request more data.
	return 0, nil, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes 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 app does all of the work necessary to create a Kubernetes
// APIServer by binding together the API, master and APIServer infrastructure.
// It can be configured and called directly or via the hyperkube framework.
package app

import (
	"strings"

	"github.com/golang/glog"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"

	"k8s.io/kubernetes/pkg/admission"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/apiserver"
	"k8s.io/kubernetes/pkg/apiserver/authenticator"
	"k8s.io/kubernetes/pkg/genericapiserver"
	genericoptions "k8s.io/kubernetes/pkg/genericapiserver/options"
	"k8s.io/kubernetes/pkg/registry/cachesize"
	"k8s.io/kubernetes/pkg/registry/generic"
)

// NewAPIServerCommand creates a *cobra.Command object with default parameters
func NewAPIServerCommand() *cobra.Command {
	s := genericoptions.NewServerRunOptions()
	s.AddFlags(pflag.CommandLine)
	cmd := &cobra.Command{
		Use: "federation-apiserver",
		Long: `The Kubernetes federation API server validates and configures data
for the api objects which include pods, services, replicationcontrollers, and
others. The API Server services REST operations and provides the frontend to the
cluster's shared state through which all other components interact.`,
		Run: func(cmd *cobra.Command, args []string) {
		},
	}

	return cmd
}

// Run runs the specified APIServer.  This should never exit.
func Run(s *genericoptions.ServerRunOptions) error {
	genericapiserver.DefaultAndValidateRunOptions(s)

	// TODO: register cluster federation resources here.
	resourceConfig := genericapiserver.NewResourceConfig()

	storageGroupsToEncodingVersion, err := s.StorageGroupsToEncodingVersion()
	if err != nil {
		glog.Fatalf("error generating storage version map: %s", err)
	}
	storageFactory, err := genericapiserver.BuildDefaultStorageFactory(
		s.StorageConfig, s.DefaultStorageMediaType, api.Codecs,
		genericapiserver.NewDefaultResourceEncodingConfig(), storageGroupsToEncodingVersion,
		resourceConfig, s.RuntimeConfig)
	if err != nil {
		glog.Fatalf("error in initializing storage factory: %s", err)
	}

	for _, override := range s.EtcdServersOverrides {
		tokens := strings.Split(override, "#")
		if len(tokens) != 2 {
			glog.Errorf("invalid value of etcd server overrides: %s", override)
			continue
		}

		apiresource := strings.Split(tokens[0], "/")
		if len(apiresource) != 2 {
			glog.Errorf("invalid resource definition: %s", tokens[0])
			continue
		}
		group := apiresource[0]
		resource := apiresource[1]
		groupResource := unversioned.GroupResource{Group: group, Resource: resource}

		servers := strings.Split(tokens[1], ";")
		storageFactory.SetEtcdLocation(groupResource, servers)
	}

	authenticator, err := authenticator.New(authenticator.AuthenticatorConfig{
		BasicAuthFile:     s.BasicAuthFile,
		ClientCAFile:      s.ClientCAFile,
		TokenAuthFile:     s.TokenAuthFile,
		OIDCIssuerURL:     s.OIDCIssuerURL,
		OIDCClientID:      s.OIDCClientID,
		OIDCCAFile:        s.OIDCCAFile,
		OIDCUsernameClaim: s.OIDCUsernameClaim,
		OIDCGroupsClaim:   s.OIDCGroupsClaim,
		KeystoneURL:       s.KeystoneURL,
	})
	if err != nil {
		glog.Fatalf("Invalid Authentication Config: %v", err)
	}

	authorizationModeNames := strings.Split(s.AuthorizationMode, ",")
	authorizer, err := apiserver.NewAuthorizerFromAuthorizationConfig(authorizationModeNames, s.AuthorizationConfig)
	if err != nil {
		glog.Fatalf("Invalid Authorization Config: %v", err)
	}

	admissionControlPluginNames := strings.Split(s.AdmissionControl, ",")
	client, err := s.NewSelfClient()
	if err != nil {
		glog.Errorf("Failed to create clientset: %v", err)
	}
	admissionController := admission.NewFromPlugins(client, admissionControlPluginNames, s.AdmissionControlConfigFile)

	genericConfig := genericapiserver.NewConfig(s)
	// TODO: Move the following to generic api server as well.
	genericConfig.StorageFactory = storageFactory
	genericConfig.Authenticator = authenticator
	genericConfig.SupportsBasicAuth = len(s.BasicAuthFile) > 0
	genericConfig.Authorizer = authorizer
	genericConfig.AdmissionControl = admissionController
	genericConfig.APIResourceConfigSource = storageFactory.APIResourceConfigSource
	genericConfig.MasterServiceNamespace = s.MasterServiceNamespace
	genericConfig.Serializer = api.Codecs

	// TODO: Move this to generic api server (Need to move the command line flag).
	if s.EnableWatchCache {
		cachesize.SetWatchCacheSizes(s.WatchCacheSizes)
	}

	m, err := genericapiserver.New(genericConfig)
	if err != nil {
		return err
	}

	installFederationAPIs(s, m, storageFactory)
	installCoreAPIs(s, m, storageFactory)
	installExtensionsAPIs(s, m, storageFactory)

	m.Run(s)
	return nil
}

func createRESTOptionsOrDie(s *genericoptions.ServerRunOptions, g *genericapiserver.GenericAPIServer, f genericapiserver.StorageFactory, resource unversioned.GroupResource) generic.RESTOptions {
	storage, err := f.New(resource)
	if err != nil {
		glog.Fatalf("Unable to find storage destination for %v, due to %v", resource, err.Error())
	}
	return generic.RESTOptions{
		Storage:                 storage,
		Decorator:               g.StorageDecorator(),
		DeleteCollectionWorkers: s.DeleteCollectionWorkers,
	}
}
<commit_msg>Allow shareable resources for admission control plugins<commit_after>/*
Copyright 2014 The Kubernetes 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 app does all of the work necessary to create a Kubernetes
// APIServer by binding together the API, master and APIServer infrastructure.
// It can be configured and called directly or via the hyperkube framework.
package app

import (
	"strings"
	"time"

	"github.com/golang/glog"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"

	"k8s.io/kubernetes/pkg/admission"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/unversioned"
	"k8s.io/kubernetes/pkg/apiserver"
	"k8s.io/kubernetes/pkg/apiserver/authenticator"
	"k8s.io/kubernetes/pkg/controller/framework/informers"
	"k8s.io/kubernetes/pkg/genericapiserver"
	genericoptions "k8s.io/kubernetes/pkg/genericapiserver/options"
	"k8s.io/kubernetes/pkg/registry/cachesize"
	"k8s.io/kubernetes/pkg/registry/generic"
)

// NewAPIServerCommand creates a *cobra.Command object with default parameters
func NewAPIServerCommand() *cobra.Command {
	s := genericoptions.NewServerRunOptions()
	s.AddFlags(pflag.CommandLine)
	cmd := &cobra.Command{
		Use: "federation-apiserver",
		Long: `The Kubernetes federation API server validates and configures data
for the api objects which include pods, services, replicationcontrollers, and
others. The API Server services REST operations and provides the frontend to the
cluster's shared state through which all other components interact.`,
		Run: func(cmd *cobra.Command, args []string) {
		},
	}

	return cmd
}

// Run runs the specified APIServer.  This should never exit.
func Run(s *genericoptions.ServerRunOptions) error {
	genericapiserver.DefaultAndValidateRunOptions(s)

	// TODO: register cluster federation resources here.
	resourceConfig := genericapiserver.NewResourceConfig()

	storageGroupsToEncodingVersion, err := s.StorageGroupsToEncodingVersion()
	if err != nil {
		glog.Fatalf("error generating storage version map: %s", err)
	}
	storageFactory, err := genericapiserver.BuildDefaultStorageFactory(
		s.StorageConfig, s.DefaultStorageMediaType, api.Codecs,
		genericapiserver.NewDefaultResourceEncodingConfig(), storageGroupsToEncodingVersion,
		resourceConfig, s.RuntimeConfig)
	if err != nil {
		glog.Fatalf("error in initializing storage factory: %s", err)
	}

	for _, override := range s.EtcdServersOverrides {
		tokens := strings.Split(override, "#")
		if len(tokens) != 2 {
			glog.Errorf("invalid value of etcd server overrides: %s", override)
			continue
		}

		apiresource := strings.Split(tokens[0], "/")
		if len(apiresource) != 2 {
			glog.Errorf("invalid resource definition: %s", tokens[0])
			continue
		}
		group := apiresource[0]
		resource := apiresource[1]
		groupResource := unversioned.GroupResource{Group: group, Resource: resource}

		servers := strings.Split(tokens[1], ";")
		storageFactory.SetEtcdLocation(groupResource, servers)
	}

	authenticator, err := authenticator.New(authenticator.AuthenticatorConfig{
		BasicAuthFile:     s.BasicAuthFile,
		ClientCAFile:      s.ClientCAFile,
		TokenAuthFile:     s.TokenAuthFile,
		OIDCIssuerURL:     s.OIDCIssuerURL,
		OIDCClientID:      s.OIDCClientID,
		OIDCCAFile:        s.OIDCCAFile,
		OIDCUsernameClaim: s.OIDCUsernameClaim,
		OIDCGroupsClaim:   s.OIDCGroupsClaim,
		KeystoneURL:       s.KeystoneURL,
	})
	if err != nil {
		glog.Fatalf("Invalid Authentication Config: %v", err)
	}

	authorizationModeNames := strings.Split(s.AuthorizationMode, ",")
	authorizer, err := apiserver.NewAuthorizerFromAuthorizationConfig(authorizationModeNames, s.AuthorizationConfig)
	if err != nil {
		glog.Fatalf("Invalid Authorization Config: %v", err)
	}

	admissionControlPluginNames := strings.Split(s.AdmissionControl, ",")
	client, err := s.NewSelfClient()
	if err != nil {
		glog.Errorf("Failed to create clientset: %v", err)
	}
	namespaceInformer := informers.CreateSharedNamespaceIndexInformer(client, 5*time.Minute)
	pluginInit := admission.NewPluginInitializer()
	pluginInit.SetNamespaceInformer(namespaceInformer)

	admissionController := admission.NewFromPlugins(client, admissionControlPluginNames, s.AdmissionControlConfigFile)
	genericConfig := genericapiserver.NewConfig(s)
	// TODO: Move the following to generic api server as well.
	genericConfig.StorageFactory = storageFactory
	genericConfig.Authenticator = authenticator
	genericConfig.SupportsBasicAuth = len(s.BasicAuthFile) > 0
	genericConfig.Authorizer = authorizer
	genericConfig.AdmissionControl = admissionController
	genericConfig.APIResourceConfigSource = storageFactory.APIResourceConfigSource
	genericConfig.MasterServiceNamespace = s.MasterServiceNamespace
	genericConfig.Serializer = api.Codecs

	// TODO: Move this to generic api server (Need to move the command line flag).
	if s.EnableWatchCache {
		cachesize.SetWatchCacheSizes(s.WatchCacheSizes)
	}

	m, err := genericapiserver.New(genericConfig)
	if err != nil {
		return err
	}

	installFederationAPIs(s, m, storageFactory)
	installCoreAPIs(s, m, storageFactory)
	installExtensionsAPIs(s, m, storageFactory)

	m.Run(s)
	return nil
}

func createRESTOptionsOrDie(s *genericoptions.ServerRunOptions, g *genericapiserver.GenericAPIServer, f genericapiserver.StorageFactory, resource unversioned.GroupResource) generic.RESTOptions {
	storage, err := f.New(resource)
	if err != nil {
		glog.Fatalf("Unable to find storage destination for %v, due to %v", resource, err.Error())
	}
	return generic.RESTOptions{
		Storage:                 storage,
		Decorator:               g.StorageDecorator(),
		DeleteCollectionWorkers: s.DeleteCollectionWorkers,
	}
}
<|endoftext|>


<commit_before>package command

import (
	"errors"
	"fmt"
	"html/template"
	"os"
	"strconv"
	"strings"
	"text/tabwriter"

	client "github.com/openebs/maya/pkg/client/jiva"
	k8sclient "github.com/openebs/maya/pkg/client/k8s"
	"github.com/openebs/maya/pkg/util"
	"github.com/openebs/maya/types/v1"
	"github.com/spf13/cobra"
)

var (
	volumeInfoCommandHelpText = `
	    Usage: mayactl volume info --volname <vol>

        This command fetches the information and status of the various
	    aspects of the Volume such as ISCSI, Controller and Replica.
        `
)

//values keeps info of the values of a current address in replicaIPStatus map
type Value struct {
	index  int
	status string
	mode   string
}

// PortalInfo keep info about the ISCSI Target Portal.
type PortalInfo struct {
	IQN        string
	VolumeName string
	Portal     string
	Size       string
	Status     string
}

// ReplicaInfo keep info about the replicas.
type ReplicaInfo struct {
	IP         string
	AccessMode string
	Status     string
	Name       string
	NodeName   string
}

// CmdVolumeInfoOptions is used to store the value of flags used in the cli
type CmdVolumeInfoOptions struct {
	volName string
}

// NewCmdVolumeInfo shows info of OpenEBS Volume
func NewCmdVolumeInfo() *cobra.Command {
	options := CmdVolumeInfoOptions{}
	cmd := &cobra.Command{
		Use:     "info",
		Short:   "Displays the info of Volume",
		Long:    volumeInfoCommandHelpText,
		Example: `mayactl volume info --volname <vol>`,
		Run: func(cmd *cobra.Command, args []string) {
			util.CheckErr(options.Validate(cmd), util.Fatal)
			util.CheckErr(options.RunVolumeInfo(cmd), util.Fatal)
		},
	}
	cmd.Flags().StringVarP(&options.volName, "volname", "n", options.volName,
		"unique volume name.")

	return cmd
}

// Validate verifies the command whether volName is passed or not.
func (c *CmdVolumeInfoOptions) Validate(cmd *cobra.Command) error {
	if c.volName == "" {
		return errors.New("--volname is missing. Please try running [mayactl volume list] to see list of volumes.")
	}
	return nil
}

// TODO : Add more volume information
// RunVolumeInfo runs info command and make call to DisplayVolumeInfo
func (c *CmdVolumeInfoOptions) RunVolumeInfo(cmd *cobra.Command) error {
	annotation := &Annotations{}
	// GetVolumeAnnotation is called to get the volume controller's info such as
	// controller's IP, status, iqn, replica IPs etc.
	err := annotation.GetVolAnnotations(c.volName)
	if err != nil {
		return nil
	}
	if annotation.ControllerStatus != "Running" {
		fmt.Printf("Unable to fetch volume details, Volume controller's status is '%s'.\n", annotation.ControllerStatus)
		return nil
	}

	// Initiallize an instance of ReplicaCollection, json response recieved from the
	// controllerIP:9501/v1/replicas is to be parsed into this structure via GetVolumeStats.
	// An API needs to be passed as argument.
	collection := client.ReplicaCollection{}
	controllerClient := client.ControllerClient{}
	_, err = controllerClient.GetVolumeStats(annotation.ClusterIP+v1.ControllerPort, v1.InfoAPI, &collection)
	if err != nil {
		return err
	}

	c.DisplayVolumeInfo(annotation, collection)
	return nil
}

func updateReplicasInfo(replicaInfo map[int]*ReplicaInfo) error {
	K8sClient, err := k8sclient.NewK8sClient("")
	if err != nil {
		return err
	}

	pods, err := K8sClient.GetPods()
	if err != nil {
		return err
	}

	for _, replica := range replicaInfo {
		for _, pod := range pods {
			if pod.Status.PodIP == replica.IP {
				replica.NodeName = pod.Spec.NodeName
				replica.Name = pod.ObjectMeta.Name
			}
		}
	}

	return nil
}

// DisplayVolumeInfo displays the outputs in standard I/O.
// Currently It displays volume access modes and target portal details only.
func (c *CmdVolumeInfoOptions) DisplayVolumeInfo(a *Annotations, collection client.ReplicaCollection) error {
	var (
		// address, mode are used here as blackbox for the replica info
		// address keeps the ip and access mode details respectively.
		address, mode []string
		replicaCount  int
		portalInfo    PortalInfo
	)
	const (
		replicaTemplate = `
<<<<<<< 47d72fc44065a7693c2b53a6ddd45a234114afdf

Replica Details :
---------------- {{range $key, $value := .}}
{{ printf "%s\t" $value.Name }} {{ printf "%s\t" $value.AccessMode }} {{ printf "%s\t" $value.Status }} {{ printf "%s\t" $value.IP }} {{ $value.NodeName }} {{end}}
=======
============================================== Replica Details ==============================================
{{range $key, $value := .}}
{{ printf "%s\t" $value.Name }} {{ printf "%s\t" $value.AccessMode }} {{ printf "%s\t" $value.Status }} {{ printf "%s\t" $value.IP }} {{ $value.NodeName }} {{end}}
=============================================================================================================
>>>>>>> Proper alignment with the use of tabwriter
`
		portalTemplate = `
Portal Details :
---------------
IQN     :   {{.IQN}}
Volume  :   {{.VolumeName}}
Portal  :   {{.Portal}}
Size    :   {{.Size}}
Status  :   {{.Status}}
`
	)
	portalInfo = PortalInfo{
		a.Iqn,
		c.volName,
		a.TargetPortal,
		a.VolSize,
		a.ControllerStatus,
	}
	tmpl, err := template.New("VolumeInfo").Parse(portalTemplate)
	if err != nil {
		fmt.Println("Error displaying output, found error :", err)
		return nil
	}
	err = tmpl.Execute(os.Stdout, portalInfo)
	if err != nil {
		fmt.Println("Error displaying volume details, found error :", err)
		return nil
	}
	replicaCount, _ = strconv.Atoi(a.ReplicaCount)
	// This case will occur only if user has manually specified zero replica.
	if replicaCount == 0 {
		fmt.Println("None of the replicas are running, please check the volume pod's status by running [kubectl describe pod -l=openebs/replica --all-namespaces] or try again later.")
		return nil
	}

	// Splitting strings with delimiter ','
	replicaStatusStrings := strings.Split(a.ReplicaStatus, ",")
	addressIPStrings := strings.Split(a.Replicas, ",")

	// making a map of replica ip and their respective status,index and mode
	replicaIPStatus := make(map[string]*Value)
	for i, v := range addressIPStrings {
		if v != "nil" {
			replicaIPStatus[v] = &Value{index: i, status: replicaStatusStrings[i], mode: "NA"}
		} else {
			// appending address with index to avoid same key conflict
			replicaIPStatus[v+string(i)] = &Value{index: i, status: replicaStatusStrings[i], mode: "NA"}
		}
	}

	// We get the info of the running replicas from the collection.data.
	// We are appending modes if available in collection.data to replicaIPStatus

	replicaInfo := make(map[int]*ReplicaInfo)
	replicaInfo[0] = &ReplicaInfo{"IP", "ACCESSMODE", "STATUS", "NAME", "NODE"}
	replicaInfo[1] = &ReplicaInfo{"---", "-----------", "-------", "-----", "-----"}
	for key := range collection.Data {
		address = append(address, strings.TrimSuffix(strings.TrimPrefix(collection.Data[key].Address, "tcp://"), v1.ReplicaPort))
		mode = append(mode, collection.Data[key].Mode)
		replicaIPStatus[address[key]].mode = mode[key]

	}

	for k, v := range replicaIPStatus {
		// checking if the first three letters is nil or not if it is nil then the ip is not avaiable
		if k[0:3] != "nil" {
			replicaInfo[v.index+2] = &ReplicaInfo{k, v.mode, v.status, "Error fetching Name", "Error Fetching Node"}
		} else {
			replicaInfo[v.index+2] = &ReplicaInfo{"NA", v.mode, v.status, "Error fetching Name", "Error Fetching Node"}
		}
	}

	err = updateReplicasInfo(replicaInfo)
	if err != nil {
		fmt.Println("Error in getting information from K8s. Please try again")
	}

<<<<<<< 47d72fc44065a7693c2b53a6ddd45a234114afdf
	tmpl = template.New("ReplicaInfo")
	tmpl = template.Must(tmpl.Parse(replicaTemplate))

	w := tabwriter.NewWriter(os.Stdout, 0, 0, 4, ' ', 0)
=======
	if length < replicaCount {
		for i := length; i < (replicaCount); i++ {
			replicaInfo[i+1] = &ReplicaInfo{"NA", "NA", replicaStatus[i], "NA", "NA"}
		}
	}
	tmpl = template.New("ReplicaInfo")
	tmpl = template.Must(tmpl.Parse(replicaTemplate))

	w := tabwriter.NewWriter(os.Stdout, 0, 0, 8, ' ', 0)
>>>>>>> Proper alignment with the use of tabwriter
	err = tmpl.Execute(w, replicaInfo)
	if err != nil {
		fmt.Println("Unable to display volume info, found error : ", err)
	}
	w.Flush()

	return nil
}
<commit_msg>Improve template view in mayactl volume info command<commit_after>package command

import (
	"errors"
	"fmt"
	"html/template"
	"os"
	"strconv"
	"strings"
	"text/tabwriter"

	client "github.com/openebs/maya/pkg/client/jiva"
	k8sclient "github.com/openebs/maya/pkg/client/k8s"
	"github.com/openebs/maya/pkg/util"
	"github.com/openebs/maya/types/v1"
	"github.com/spf13/cobra"
)

var (
	volumeInfoCommandHelpText = `
	    Usage: mayactl volume info --volname <vol>

        This command fetches the information and status of the various
	    aspects of the Volume such as ISCSI, Controller and Replica.
        `
)

//values keeps info of the values of a current address in replicaIPStatus map
type Value struct {
	index  int
	status string
	mode   string
}

// PortalInfo keep info about the ISCSI Target Portal.
type PortalInfo struct {
	IQN        string
	VolumeName string
	Portal     string
	Size       string
	Status     string
}

// ReplicaInfo keep info about the replicas.
type ReplicaInfo struct {
	IP         string
	AccessMode string
	Status     string
	Name       string
	NodeName   string
}

// CmdVolumeInfoOptions is used to store the value of flags used in the cli
type CmdVolumeInfoOptions struct {
	volName string
}

// NewCmdVolumeInfo shows info of OpenEBS Volume
func NewCmdVolumeInfo() *cobra.Command {
	options := CmdVolumeInfoOptions{}
	cmd := &cobra.Command{
		Use:     "info",
		Short:   "Displays the info of Volume",
		Long:    volumeInfoCommandHelpText,
		Example: `mayactl volume info --volname <vol>`,
		Run: func(cmd *cobra.Command, args []string) {
			util.CheckErr(options.Validate(cmd), util.Fatal)
			util.CheckErr(options.RunVolumeInfo(cmd), util.Fatal)
		},
	}
	cmd.Flags().StringVarP(&options.volName, "volname", "n", options.volName,
		"unique volume name.")

	return cmd
}

// Validate verifies the command whether volName is passed or not.
func (c *CmdVolumeInfoOptions) Validate(cmd *cobra.Command) error {
	if c.volName == "" {
		return errors.New("--volname is missing. Please try running [mayactl volume list] to see list of volumes.")
	}
	return nil
}

// TODO : Add more volume information
// RunVolumeInfo runs info command and make call to DisplayVolumeInfo
func (c *CmdVolumeInfoOptions) RunVolumeInfo(cmd *cobra.Command) error {
	annotation := &Annotations{}
	// GetVolumeAnnotation is called to get the volume controller's info such as
	// controller's IP, status, iqn, replica IPs etc.
	err := annotation.GetVolAnnotations(c.volName)
	if err != nil {
		return nil
	}
	if annotation.ControllerStatus != "Running" {
		fmt.Printf("Unable to fetch volume details, Volume controller's status is '%s'.\n", annotation.ControllerStatus)
		return nil
	}

	// Initiallize an instance of ReplicaCollection, json response recieved from the
	// controllerIP:9501/v1/replicas is to be parsed into this structure via GetVolumeStats.
	// An API needs to be passed as argument.
	collection := client.ReplicaCollection{}
	controllerClient := client.ControllerClient{}
	_, err = controllerClient.GetVolumeStats(annotation.ClusterIP+v1.ControllerPort, v1.InfoAPI, &collection)
	if err != nil {
		return err
	}

	c.DisplayVolumeInfo(annotation, collection)
	return nil
}

func updateReplicasInfo(replicaInfo map[int]*ReplicaInfo) error {
	K8sClient, err := k8sclient.NewK8sClient("")
	if err != nil {
		return err
	}

	pods, err := K8sClient.GetPods()
	if err != nil {
		return err
	}

	for _, replica := range replicaInfo {
		for _, pod := range pods {
			if pod.Status.PodIP == replica.IP {
				replica.NodeName = pod.Spec.NodeName
				replica.Name = pod.ObjectMeta.Name
			}
		}
	}

	return nil
}

// DisplayVolumeInfo displays the outputs in standard I/O.
// Currently It displays volume access modes and target portal details only.
func (c *CmdVolumeInfoOptions) DisplayVolumeInfo(a *Annotations, collection client.ReplicaCollection) error {
	var (
		// address, mode are used here as blackbox for the replica info
		// address keeps the ip and access mode details respectively.
		address, mode []string
		replicaCount  int
		portalInfo    PortalInfo
	)
	const (
		replicaTemplate = `

Replica Details :
---------------- {{range $key, $value := .}}
{{ printf "%s\t" $value.Name }} {{ printf "%s\t" $value.AccessMode }} {{ printf "%s\t" $value.Status }} {{ printf "%s\t" $value.IP }} {{ $value.NodeName }} {{end}}
`
		portalTemplate = `
Portal Details :
---------------
IQN     :   {{.IQN}}
Volume  :   {{.VolumeName}}
Portal  :   {{.Portal}}
Size    :   {{.Size}}
Status  :   {{.Status}}
`
	)
	portalInfo = PortalInfo{
		a.Iqn,
		c.volName,
		a.TargetPortal,
		a.VolSize,
		a.ControllerStatus,
	}
	tmpl, err := template.New("VolumeInfo").Parse(portalTemplate)
	if err != nil {
		fmt.Println("Error displaying output, found error :", err)
		return nil
	}
	err = tmpl.Execute(os.Stdout, portalInfo)
	if err != nil {
		fmt.Println("Error displaying volume details, found error :", err)
		return nil
	}
	replicaCount, _ = strconv.Atoi(a.ReplicaCount)
	// This case will occur only if user has manually specified zero replica.
	if replicaCount == 0 {
		fmt.Println("None of the replicas are running, please check the volume pod's status by running [kubectl describe pod -l=openebs/replica --all-namespaces] or try again later.")
		return nil
	}

	// Splitting strings with delimiter ','
	replicaStatusStrings := strings.Split(a.ReplicaStatus, ",")
	addressIPStrings := strings.Split(a.Replicas, ",")

	// making a map of replica ip and their respective status,index and mode
	replicaIPStatus := make(map[string]*Value)
	for i, v := range addressIPStrings {
		if v != "nil" {
			replicaIPStatus[v] = &Value{index: i, status: replicaStatusStrings[i], mode: "NA"}
		} else {
			// appending address with index to avoid same key conflict
			replicaIPStatus[v+string(i)] = &Value{index: i, status: replicaStatusStrings[i], mode: "NA"}
		}
	}

	// We get the info of the running replicas from the collection.data.
	// We are appending modes if available in collection.data to replicaIPStatus

	replicaInfo := make(map[int]*ReplicaInfo)
	replicaInfo[0] = &ReplicaInfo{"IP", "ACCESSMODE", "STATUS", "NAME", "NODE"}
	replicaInfo[1] = &ReplicaInfo{"---", "-----------", "-------", "-----", "-----"}
	for key := range collection.Data {
		address = append(address, strings.TrimSuffix(strings.TrimPrefix(collection.Data[key].Address, "tcp://"), v1.ReplicaPort))
		mode = append(mode, collection.Data[key].Mode)
		replicaIPStatus[address[key]].mode = mode[key]
	}

	for k, v := range replicaIPStatus {
		// checking if the first three letters is nil or not if it is nil then the ip is not avaiable
		if k[0:3] != "nil" {
			replicaInfo[v.index+2] = &ReplicaInfo{k, v.mode, v.status, "Error fetching Name", "Error Fetching Node"}
		} else {
			replicaInfo[v.index+2] = &ReplicaInfo{"NA", v.mode, v.status, "Error fetching Name", "Error Fetching Node"}
		}
	}

	err = updateReplicasInfo(replicaInfo)
	if err != nil {
		fmt.Println("Error in getting information from K8s. Please try again")
	}

	tmpl = template.New("ReplicaInfo")
	tmpl = template.Must(tmpl.Parse(replicaTemplate))

	w := tabwriter.NewWriter(os.Stdout, 0, 0, 4, ' ', 0)
	err = tmpl.Execute(w, replicaInfo)
	if err != nil {
		fmt.Println("Unable to display volume info, found error : ", err)
	}
	w.Flush()

	return nil
}
<|endoftext|>


<commit_before>package config

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net"
	"strings"

	"github.com/EngineerBetter/concourse-up/commands/deploy"
	"github.com/EngineerBetter/concourse-up/iaas"
	"github.com/asaskevich/govalidator"
)

const terraformStateFileName = "terraform.tfstate"
const configFilePath = "config.json"

// IClient is an interface for the config file client
type IClient interface {
	Load() (Config, error)
	DeleteAll(config Config) error
	LoadOrCreate(deployArgs *deploy.Args) (Config, bool, bool, error)
	Update(Config) error
	StoreAsset(filename string, contents []byte) error
	HasAsset(filename string) (bool, error)
	LoadAsset(filename string) ([]byte, error)
	DeleteAsset(filename string) error
}

// Client is a client for loading the config file  from S3
type Client struct {
	Iaas         iaas.Provider
	Project      string
	Namespace    string
	BucketName   string
	BucketExists bool
	BucketError  error
	Config       *Config
}

// New instantiates a new client
func New(iaas iaas.Provider, project, namespace string) *Client {
	namespace = determineNamespace(namespace, iaas.Region())
	bucketName, exists, err := determineBucketName(iaas, namespace, project)

	if !exists && err == nil {
		err = iaas.CreateBucket(bucketName)
	}

	return &Client{
		iaas,
		project,
		namespace,
		bucketName,
		exists,
		err,
		&Config{},
	}
}

// StoreAsset stores an associated configuration file
func (client *Client) StoreAsset(filename string, contents []byte) error {
	return client.Iaas.WriteFile(client.configBucket(),
		filename,
		contents,
	)
}

// LoadAsset loads an associated configuration file
func (client *Client) LoadAsset(filename string) ([]byte, error) {
	return client.Iaas.LoadFile(
		client.configBucket(),
		filename,
	)
}

// DeleteAsset deletes an associated configuration file
func (client *Client) DeleteAsset(filename string) error {
	return client.Iaas.DeleteFile(
		client.configBucket(),
		filename,
	)
}

// HasAsset returns true if an associated configuration file exists
func (client *Client) HasAsset(filename string) (bool, error) {
	return client.Iaas.HasFile(
		client.configBucket(),
		filename,
	)
}

// Update stores the conconcourse up config file to S3
func (client *Client) Update(config Config) error {
	bytes, err := json.Marshal(config)
	if err != nil {
		return err
	}

	return client.Iaas.WriteFile(client.configBucket(), configFilePath, bytes)
}

// DeleteAll deletes the entire configuration bucket
func (client *Client) DeleteAll(config Config) error {
	return client.Iaas.DeleteVersionedBucket(config.ConfigBucket)
}

// Load loads an existing config file from S3
func (client *Client) Load() (Config, error) {
	if client.BucketError != nil {
		return Config{}, client.BucketError
	}

	configBytes, err := client.Iaas.LoadFile(
		client.configBucket(),
		configFilePath,
	)
	if err != nil {
		return Config{}, err
	}

	conf := Config{}
	if err := json.Unmarshal(configBytes, &conf); err != nil {
		return Config{}, err
	}

	return conf, nil
}

// LoadOrCreate loads an existing config file from S3, or creates a default if one doesn't already exist
func (client *Client) LoadOrCreate(deployArgs *deploy.Args) (Config, bool, bool, error) {

	var isDomainUpdated bool

	if client.BucketError != nil {
		return Config{}, false, false, client.BucketError
	}

	config, err := generateDefaultConfig(
		client.Project,
		deployment(client.Project),
		client.configBucket(),
		client.Iaas.Region(),
		client.Namespace,
	)
	if err != nil {
		return Config{}, false, false, err
	}

	defaultConfigBytes, err := json.Marshal(&config)
	if err != nil {
		return Config{}, false, false, err
	}

	configBytes, newConfigCreated, err := client.Iaas.EnsureFileExists(
		client.configBucket(),
		configFilePath,
		defaultConfigBytes,
	)
	if err != nil {
		return Config{}, newConfigCreated, false, err
	}

	err = json.Unmarshal(configBytes, &config)
	if err != nil {
		return Config{}, newConfigCreated, false, err
	}

	allow, err := parseCIDRBlocks(deployArgs.AllowIPs)
	if err != nil {
		return Config{}, newConfigCreated, false, err
	}

	config, err = updateAllowedIPs(config, allow)
	if err != nil {
		return Config{}, newConfigCreated, false, err
	}

	if newConfigCreated {
		config.IAAS = deployArgs.IAAS
	}

	if deployArgs.ZoneIsSet {
		// This is a safeguard for a redeployment where zone does not belong to the region where the original deployment has happened
		if !newConfigCreated && deployArgs.Zone != config.AvailabilityZone {
			return Config{}, false, false, fmt.Errorf("Existing deployment uses zone %s and cannot change to zone %s", config.AvailabilityZone, deployArgs.Zone)
		}
		config.AvailabilityZone = deployArgs.Zone
	}
	if newConfigCreated || deployArgs.WorkerCountIsSet {
		config.ConcourseWorkerCount = deployArgs.WorkerCount
	}
	if newConfigCreated || deployArgs.WorkerSizeIsSet {
		config.ConcourseWorkerSize = deployArgs.WorkerSize
	}
	if newConfigCreated || deployArgs.WebSizeIsSet {
		config.ConcourseWebSize = deployArgs.WebSize
	}
	if newConfigCreated || deployArgs.DBSizeIsSet {
		config.RDSInstanceClass = client.Iaas.DBType(deployArgs.DBSize)
	}
	if newConfigCreated || deployArgs.GithubAuthIsSet {
		config.GithubClientID = deployArgs.GithubAuthClientID
		config.GithubClientSecret = deployArgs.GithubAuthClientSecret
		config.GithubAuthIsSet = deployArgs.GithubAuthIsSet
	}
	if newConfigCreated || deployArgs.TagsIsSet {
		config.Tags = deployArgs.Tags
	}
	if newConfigCreated || deployArgs.SpotIsSet {
		config.Spot = deployArgs.Spot && deployArgs.Preemptible
	}
	if newConfigCreated || deployArgs.WorkerTypeIsSet {
		config.WorkerType = deployArgs.WorkerType
	}

	if newConfigCreated || deployArgs.DomainIsSet {
		if config.Domain != deployArgs.Domain {
			isDomainUpdated = true
		}
		config.Domain = deployArgs.Domain
	} else {
		if govalidator.IsIPv4(config.Domain) {
			config.Domain = ""
		}
	}
	return config, newConfigCreated, isDomainUpdated, nil
}

func (client *Client) configBucket() string {
	return client.BucketName
}

func deployment(project string) string {
	return fmt.Sprintf("concourse-up-%s", project)
}

func createBucketName(deployment, extension string) string {
	return fmt.Sprintf("%s-%s-config", deployment, extension)
}

func determineBucketName(iaas iaas.Provider, namespace, project string) (string, bool, error) {
	regionBucketName := createBucketName(deployment(project), iaas.Region())
	namespaceBucketName := createBucketName(deployment(project), namespace)

	foundRegionNamedBucket, err := iaas.BucketExists(regionBucketName)
	if err != nil {
		return "", false, err
	}
	foundNamespacedBucket, err := iaas.BucketExists(namespaceBucketName)
	if err != nil {
		return "", false, err
	}

	foundOne := foundRegionNamedBucket || foundNamespacedBucket

	switch {
	case !foundRegionNamedBucket && foundNamespacedBucket:
		return namespaceBucketName, foundOne, nil
	case foundRegionNamedBucket && !foundNamespacedBucket:
		return regionBucketName, foundOne, nil
	default:
		return namespaceBucketName, foundOne, nil
	}
}

type cidrBlocks []*net.IPNet

func parseCIDRBlocks(s string) (cidrBlocks, error) {
	var x cidrBlocks
	for _, ip := range strings.Split(s, ",") {
		ip = strings.TrimSpace(ip)
		_, ipNet, err := net.ParseCIDR(ip)
		if err != nil {
			ipNet = &net.IPNet{
				IP:   net.ParseIP(ip),
				Mask: net.CIDRMask(32, 32),
			}
		}
		if ipNet.IP == nil {
			return nil, fmt.Errorf("could not parse %q as an IP address or CIDR range", ip)
		}
		x = append(x, ipNet)
	}
	return x, nil
}

func (b cidrBlocks) String() (string, error) {
	var buf bytes.Buffer
	for i, ipNet := range b {
		if i > 0 {
			_, err := fmt.Fprintf(&buf, ", %q", ipNet)
			if err != nil {
				return "", err
			}
		} else {
			_, err := fmt.Fprintf(&buf, "%q", ipNet)
			if err != nil {
				return "", err
			}
		}
	}
	return buf.String(), nil
}

func determineNamespace(namespace, region string) string {
	if namespace == "" {
		return region
	}
	return namespace
}
<commit_msg>sets IAAS in config<commit_after>package config

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net"
	"strings"

	"github.com/EngineerBetter/concourse-up/commands/deploy"
	"github.com/EngineerBetter/concourse-up/iaas"
	"github.com/asaskevich/govalidator"
)

const terraformStateFileName = "terraform.tfstate"
const configFilePath = "config.json"

// IClient is an interface for the config file client
type IClient interface {
	Load() (Config, error)
	DeleteAll(config Config) error
	LoadOrCreate(deployArgs *deploy.Args) (Config, bool, bool, error)
	Update(Config) error
	StoreAsset(filename string, contents []byte) error
	HasAsset(filename string) (bool, error)
	LoadAsset(filename string) ([]byte, error)
	DeleteAsset(filename string) error
}

// Client is a client for loading the config file  from S3
type Client struct {
	Iaas         iaas.Provider
	Project      string
	Namespace    string
	BucketName   string
	BucketExists bool
	BucketError  error
	Config       *Config
}

// New instantiates a new client
func New(iaas iaas.Provider, project, namespace string) *Client {
	namespace = determineNamespace(namespace, iaas.Region())
	bucketName, exists, err := determineBucketName(iaas, namespace, project)

	if !exists && err == nil {
		err = iaas.CreateBucket(bucketName)
	}

	return &Client{
		iaas,
		project,
		namespace,
		bucketName,
		exists,
		err,
		&Config{},
	}
}

// StoreAsset stores an associated configuration file
func (client *Client) StoreAsset(filename string, contents []byte) error {
	return client.Iaas.WriteFile(client.configBucket(),
		filename,
		contents,
	)
}

// LoadAsset loads an associated configuration file
func (client *Client) LoadAsset(filename string) ([]byte, error) {
	return client.Iaas.LoadFile(
		client.configBucket(),
		filename,
	)
}

// DeleteAsset deletes an associated configuration file
func (client *Client) DeleteAsset(filename string) error {
	return client.Iaas.DeleteFile(
		client.configBucket(),
		filename,
	)
}

// HasAsset returns true if an associated configuration file exists
func (client *Client) HasAsset(filename string) (bool, error) {
	return client.Iaas.HasFile(
		client.configBucket(),
		filename,
	)
}

// Update stores the conconcourse up config file to S3
func (client *Client) Update(config Config) error {
	bytes, err := json.Marshal(config)
	if err != nil {
		return err
	}

	return client.Iaas.WriteFile(client.configBucket(), configFilePath, bytes)
}

// DeleteAll deletes the entire configuration bucket
func (client *Client) DeleteAll(config Config) error {
	return client.Iaas.DeleteVersionedBucket(config.ConfigBucket)
}

// Load loads an existing config file from S3
func (client *Client) Load() (Config, error) {
	if client.BucketError != nil {
		return Config{}, client.BucketError
	}

	configBytes, err := client.Iaas.LoadFile(
		client.configBucket(),
		configFilePath,
	)
	if err != nil {
		return Config{}, err
	}

	conf := Config{}
	if err := json.Unmarshal(configBytes, &conf); err != nil {
		return Config{}, err
	}

	return conf, nil
}

// LoadOrCreate loads an existing config file from S3, or creates a default if one doesn't already exist
func (client *Client) LoadOrCreate(deployArgs *deploy.Args) (Config, bool, bool, error) {

	var isDomainUpdated bool

	if client.BucketError != nil {
		return Config{}, false, false, client.BucketError
	}

	config, err := generateDefaultConfig(
		client.Project,
		deployment(client.Project),
		client.configBucket(),
		client.Iaas.Region(),
		client.Namespace,
	)
	if err != nil {
		return Config{}, false, false, err
	}

	defaultConfigBytes, err := json.Marshal(&config)
	if err != nil {
		return Config{}, false, false, err
	}

	configBytes, newConfigCreated, err := client.Iaas.EnsureFileExists(
		client.configBucket(),
		configFilePath,
		defaultConfigBytes,
	)
	if err != nil {
		return Config{}, newConfigCreated, false, err
	}

	err = json.Unmarshal(configBytes, &config)
	if err != nil {
		return Config{}, newConfigCreated, false, err
	}

	allow, err := parseCIDRBlocks(deployArgs.AllowIPs)
	if err != nil {
		return Config{}, newConfigCreated, false, err
	}

	config, err = updateAllowedIPs(config, allow)
	if err != nil {
		return Config{}, newConfigCreated, false, err
	}

	if newConfigCreated {
		config.IAAS = deployArgs.IAAS
	}

	if deployArgs.ZoneIsSet {
		// This is a safeguard for a redeployment where zone does not belong to the region where the original deployment has happened
		if !newConfigCreated && deployArgs.Zone != config.AvailabilityZone {
			return Config{}, false, false, fmt.Errorf("Existing deployment uses zone %s and cannot change to zone %s", config.AvailabilityZone, deployArgs.Zone)
		}
		config.AvailabilityZone = deployArgs.Zone
	}
	if newConfigCreated {
		config.IAAS = deployArgs.IAAS
	}
	if newConfigCreated || deployArgs.WorkerCountIsSet {
		config.ConcourseWorkerCount = deployArgs.WorkerCount
	}
	if newConfigCreated || deployArgs.WorkerSizeIsSet {
		config.ConcourseWorkerSize = deployArgs.WorkerSize
	}
	if newConfigCreated || deployArgs.WebSizeIsSet {
		config.ConcourseWebSize = deployArgs.WebSize
	}
	if newConfigCreated || deployArgs.DBSizeIsSet {
		config.RDSInstanceClass = client.Iaas.DBType(deployArgs.DBSize)
	}
	if newConfigCreated || deployArgs.GithubAuthIsSet {
		config.GithubClientID = deployArgs.GithubAuthClientID
		config.GithubClientSecret = deployArgs.GithubAuthClientSecret
		config.GithubAuthIsSet = deployArgs.GithubAuthIsSet
	}
	if newConfigCreated || deployArgs.TagsIsSet {
		config.Tags = deployArgs.Tags
	}
	if newConfigCreated || deployArgs.SpotIsSet {
		config.Spot = deployArgs.Spot && deployArgs.Preemptible
	}
	if newConfigCreated || deployArgs.WorkerTypeIsSet {
		config.WorkerType = deployArgs.WorkerType
	}

	if newConfigCreated || deployArgs.DomainIsSet {
		if config.Domain != deployArgs.Domain {
			isDomainUpdated = true
		}
		config.Domain = deployArgs.Domain
	} else {
		if govalidator.IsIPv4(config.Domain) {
			config.Domain = ""
		}
	}
	return config, newConfigCreated, isDomainUpdated, nil
}

func (client *Client) configBucket() string {
	return client.BucketName
}

func deployment(project string) string {
	return fmt.Sprintf("concourse-up-%s", project)
}

func createBucketName(deployment, extension string) string {
	return fmt.Sprintf("%s-%s-config", deployment, extension)
}

func determineBucketName(iaas iaas.Provider, namespace, project string) (string, bool, error) {
	regionBucketName := createBucketName(deployment(project), iaas.Region())
	namespaceBucketName := createBucketName(deployment(project), namespace)

	foundRegionNamedBucket, err := iaas.BucketExists(regionBucketName)
	if err != nil {
		return "", false, err
	}
	foundNamespacedBucket, err := iaas.BucketExists(namespaceBucketName)
	if err != nil {
		return "", false, err
	}

	foundOne := foundRegionNamedBucket || foundNamespacedBucket

	switch {
	case !foundRegionNamedBucket && foundNamespacedBucket:
		return namespaceBucketName, foundOne, nil
	case foundRegionNamedBucket && !foundNamespacedBucket:
		return regionBucketName, foundOne, nil
	default:
		return namespaceBucketName, foundOne, nil
	}
}

type cidrBlocks []*net.IPNet

func parseCIDRBlocks(s string) (cidrBlocks, error) {
	var x cidrBlocks
	for _, ip := range strings.Split(s, ",") {
		ip = strings.TrimSpace(ip)
		_, ipNet, err := net.ParseCIDR(ip)
		if err != nil {
			ipNet = &net.IPNet{
				IP:   net.ParseIP(ip),
				Mask: net.CIDRMask(32, 32),
			}
		}
		if ipNet.IP == nil {
			return nil, fmt.Errorf("could not parse %q as an IP address or CIDR range", ip)
		}
		x = append(x, ipNet)
	}
	return x, nil
}

func (b cidrBlocks) String() (string, error) {
	var buf bytes.Buffer
	for i, ipNet := range b {
		if i > 0 {
			_, err := fmt.Fprintf(&buf, ", %q", ipNet)
			if err != nil {
				return "", err
			}
		} else {
			_, err := fmt.Fprintf(&buf, "%q", ipNet)
			if err != nil {
				return "", err
			}
		}
	}
	return buf.String(), nil
}

func determineNamespace(namespace, region string) string {
	if namespace == "" {
		return region
	}
	return namespace
}
<|endoftext|>


<commit_before>package config

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"reflect"
	"strconv"
	"strings"

	"github.com/ninjasphere/go-ninja/logger"
	"gopkg.in/alecthomas/kingpin.v1"
)

var log = logger.GetLogger("config")

var config map[string]interface{}

func init() {
	MustRefresh()
}

func GetAll(flatten bool) map[string]interface{} {
	if flatten {
		return config
	} else {
		return unflatten(config)
	}
}

var serial string

func Serial() string {
	if serial == "" {
		cmd := exec.Command("sphere-serial", os.Args[1:]...)

		var out bytes.Buffer
		cmd.Stdout = &out

		err := cmd.Run()
		if err != nil {
			log.Fatalf("Failed to get sphere serial (sphere-serial must be in the PATH) error:%s", err)
		}

		serial = out.String()
	}

	return serial
}

func String(def string, path ...string) string {
	val := get(path...)
	if val == nil {
		return def
	}
	return val.(string)
}

// MustString returns the string property at the path
func MustString(path ...string) string {
	return mustGet(path...).(string)
}

// MustStringArray returns the string array property at the path
func MustStringArray(path ...string) []string {
	a := mustGet(path...).([]interface{})
	b := make([]string, len(a))
	for i := range a {
		b[i] = a[i].(string)
	}
	return b
}

// Int returns the integer property at the path, with a default
func Int(def int, path ...string) int {
	val := get(path...)
	if val == nil {
		return def
	}
	return int(val.(float64))
}

// MustInt returns the string property at the path
func MustInt(path ...string) int {
	return int(mustGet(path...).(float64))
}

// Bool returns the boolean property at the path, with a default
func Bool(def bool, path ...string) bool {
	val := get(path...)
	if val == nil {
		return def
	}
	return val.(bool)
}

// MustBool returns the boolean property at the path
func MustBool(path ...string) bool {
	return mustGet(path...).(bool)
}

var hey = "what's up buddy?"

func HasString(path ...string) bool {
	val, ok := config[strings.Join(path, ".")]
	if !ok {
		return false
	}

	_, ok = val.(string)
	return ok
}

func mustGet(path ...string) interface{} {
	val, ok := config[strings.Join(path, ".")]
	if !ok {
		log.Fatalf("expected value for %v but found nothing", path)
	}
	return val
}

func get(path ...string) interface{} {
	val, ok := config[strings.Join(path, ".")]
	if !ok {
		return nil
	}
	return val
}

func MustRefresh() {

	flat := make(map[string]interface{})

	// cli overrides
	addArgs(flat)

	// load environments (no value args) from cli args
	environments := []string{"default"}
	for name, value := range flat {
		if value == nil {
			environments = append(environments, name)
		}
	}

	flat["env"] = environments

	userHome := getUserHome()

	// env vars (if starting with "sphere_")
	addEnv(flat)

	// anything that can be parsed as a number, is a number
	parseNumbers(flat)

	installDir := "/opt/ninjablocks"
	if val, ok := flat["installDirectory"]; ok {
		installDir = val.(string)
	}

	flat["installDirectory"] = installDir

	//sphere_configDir=/home/elliot/repos/sphere-dev/sphere-common/config

	if _, err := os.Stat(installDir); err != nil {
		log.Fatalf("Couldn't load sphere install directory. Override with env var sphere_installDirectory. error:%s", err)
	}

	// credentials file
	addFile("/etc/opt/ninja/credentials.json", flat)

	// home directory environment(s) config
	for i := len(environments) - 1; i >= 0; i-- {
		addFile(filepath.Join(userHome, ".sphere", environments[i]+".json"), flat)
	}

	// current directory environment(s) config
	for i := len(environments) - 1; i >= 0; i-- {
		addFile(filepath.Join(".", "config", environments[i]+".json"), flat)
	}

	// common directory environment(s) config
	for i := len(environments) - 1; i >= 0; i-- {
		addFile(filepath.Join(installDir, "sphere-common", "config", environments[i]+".json"), flat)
	}

	// sphere-common credentials config
	addFile(filepath.Join(installDir, "sphere-common", "config", "credentials.json"), flat)

	//log.Debugf("Loaded config: %v", flat)

	config = flat
}

func addEnv(config map[string]interface{}) {
	for _, v := range os.Environ() {
		split := strings.Split(v, "=")

		name, value := split[0], split[1]

		if strings.HasPrefix(name, "sphere_") {
			name = strings.TrimPrefix(name, "sphere_")
			name = strings.Replace(name, "_", ".", 0)

			if _, ok := config[name]; !ok {
				config[name] = value
			}
		}
	}
}

func addArgs(config map[string]interface{}) {

	parser := kingpin.Tokenize(os.Args[1:]).Tokens

	for token, parser := parser.Peek(), parser.Next(); token.Type != kingpin.TokenEOL; token, parser = parser.Peek(), parser.Next() {

		if token.IsFlag() {
			var value interface{}
			name := token.Value

			next := parser.Peek()
			if next.Type == kingpin.TokenArg {
				value = next.Value
			} else {
				// It's an environment indicator... like --cloud-production
			}

			if _, ok := config[name]; !ok {
				config[name] = value
			}
		}

	}

}

func addFile(path string, config map[string]interface{}) error {
	//log.Debugf("Loading config file: %s", path)

	file, e := ioutil.ReadFile(path)
	if e != nil {
		return fmt.Errorf("Failed to load file: %s error: %s", path, e)
	}

	content := make(map[string]interface{})
	e = json.Unmarshal(file, &content)
	if e != nil {
		return fmt.Errorf("Failed to read file: %s error: %s", path, e)
	}

	flatten(content, nil, config)
	return nil
}

func flatten(input interface{}, lpath []string, flattened map[string]interface{}) {
	if lpath == nil {
		lpath = []string{}
	}

	if reflect.ValueOf(input).Kind() == reflect.Map {
		for rkey, value := range input.(map[string]interface{}) {
			flatten(value, append(lpath, rkey), flattened)
		}
	} else {
		if _, ok := flattened[strings.Join(lpath, ".")]; !ok {
			flattened[strings.Join(lpath, ".")] = input
		}
	}
}

func unflatten(flat map[string]interface{}) map[string]interface{} {
	out := make(map[string]interface{})
	obj := out

	for key, val := range flat {

		obj = out
		keys := strings.Split(key, ".")

		for i, k := range keys {
			if i == len(keys)-1 {
				obj[k] = val
			} else {
				next, ok := obj[k]
				if !ok {
					next = make(map[string]interface{})
				}
				obj[k] = next
				obj = next.(map[string]interface{})
			}
		}

	}

	return out
}

func parseNumbers(config map[string]interface{}) {
	for name, val := range config {
		stringVal, ok := val.(string)
		if ok {
			floatVal, err := strconv.ParseFloat(stringVal, 64)
			if err == nil {
				config[name] = floatVal
			}
		}
	}
}

func getUserHome() string {
	usr, err := user.Current()
	if err != nil {
		return "/root"
	}
	return usr.HomeDir
}
<commit_msg>Restore original behaviour of sphere-config with repect to --flag type specifications.<commit_after>package config

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"reflect"
	"strconv"
	"strings"

	"github.com/ninjasphere/go-ninja/logger"
	"gopkg.in/alecthomas/kingpin.v1"
)

var log = logger.GetLogger("config")

var config map[string]interface{}

func init() {
	MustRefresh()
}

func GetAll(flatten bool) map[string]interface{} {
	if flatten {
		return config
	} else {
		return unflatten(config)
	}
}

var serial string

func Serial() string {
	if serial == "" {
		cmd := exec.Command("sphere-serial", os.Args[1:]...)

		var out bytes.Buffer
		cmd.Stdout = &out

		err := cmd.Run()
		if err != nil {
			log.Fatalf("Failed to get sphere serial (sphere-serial must be in the PATH) error:%s", err)
		}

		serial = out.String()
	}

	return serial
}

func String(def string, path ...string) string {
	val := get(path...)
	if val == nil {
		return def
	}
	return val.(string)
}

// MustString returns the string property at the path
func MustString(path ...string) string {
	return mustGet(path...).(string)
}

// MustStringArray returns the string array property at the path
func MustStringArray(path ...string) []string {
	a := mustGet(path...).([]interface{})
	b := make([]string, len(a))
	for i := range a {
		b[i] = a[i].(string)
	}
	return b
}

// Int returns the integer property at the path, with a default
func Int(def int, path ...string) int {
	val := get(path...)
	if val == nil {
		return def
	}
	return int(val.(float64))
}

// MustInt returns the string property at the path
func MustInt(path ...string) int {
	return int(mustGet(path...).(float64))
}

// Bool returns the boolean property at the path, with a default
func Bool(def bool, path ...string) bool {
	val := get(path...)
	if val == nil {
		return def
	}
	return val.(bool)
}

// MustBool returns the boolean property at the path
func MustBool(path ...string) bool {
	return mustGet(path...).(bool)
}

var hey = "what's up buddy?"

func HasString(path ...string) bool {
	val, ok := config[strings.Join(path, ".")]
	if !ok {
		return false
	}

	_, ok = val.(string)
	return ok
}

func mustGet(path ...string) interface{} {
	val, ok := config[strings.Join(path, ".")]
	if !ok {
		log.Fatalf("expected value for %v but found nothing", path)
	}
	return val
}

func get(path ...string) interface{} {
	val, ok := config[strings.Join(path, ".")]
	if !ok {
		return nil
	}
	return val
}

func MustRefresh() {

	flat := make(map[string]interface{})

	// cli overrides
	addArgs(flat)

	// load environments (no value args) from cli args
	environments := []string{"default"}
	for name, value := range flat {
		if value == nil {
			environments = append(environments, name)
		}
	}

	flat["env"] = environments

	userHome := getUserHome()

	// env vars (if starting with "sphere_")
	addEnv(flat)

	// anything that can be parsed as a number, is a number
	parseNumbers(flat)

	installDir := "/opt/ninjablocks"
	if val, ok := flat["installDirectory"]; ok {
		installDir = val.(string)
	}

	flat["installDirectory"] = installDir

	//sphere_configDir=/home/elliot/repos/sphere-dev/sphere-common/config

	if _, err := os.Stat(installDir); err != nil {
		log.Fatalf("Couldn't load sphere install directory. Override with env var sphere_installDirectory. error:%s", err)
	}

	// credentials file
	addFile("/etc/opt/ninja/credentials.json", flat)

	// home directory environment(s) config
	for i := len(environments) - 1; i >= 0; i-- {
		addFile(filepath.Join(userHome, ".sphere", environments[i]+".json"), flat)
	}

	// current directory environment(s) config
	for i := len(environments) - 1; i >= 0; i-- {
		addFile(filepath.Join(".", "config", environments[i]+".json"), flat)
	}

	// common directory environment(s) config
	for i := len(environments) - 1; i >= 0; i-- {
		addFile(filepath.Join(installDir, "sphere-common", "config", environments[i]+".json"), flat)
	}

	// sphere-common credentials config
	addFile(filepath.Join(installDir, "sphere-common", "config", "credentials.json"), flat)

	//log.Debugf("Loaded config: %v", flat)

	config = flat
}

func addEnv(config map[string]interface{}) {
	for _, v := range os.Environ() {
		split := strings.Split(v, "=")

		name, value := split[0], split[1]

		if strings.HasPrefix(name, "sphere_") {
			name = strings.TrimPrefix(name, "sphere_")
			name = strings.Replace(name, "_", ".", 0)

			if _, ok := config[name]; !ok {
				config[name] = value
			}
		}
	}
}

func addArgs(config map[string]interface{}) {

	parser := kingpin.Tokenize(os.Args[1:]).Tokens

	for token, parser := parser.Peek(), parser.Next(); token.Type != kingpin.TokenEOL; token, parser = parser.Peek(), parser.Next() {

		if token.IsFlag() {
			var value interface{}
			name := token.Value

			next := parser.Peek()
			if next.Type == kingpin.TokenArg {
				value = next.Value
			} else {
				value = true
				// It's an environment indicator... like --cloud-production
			}

			if _, ok := config[name]; !ok {
				config[name] = value
			}
		}

	}

}

func addFile(path string, config map[string]interface{}) error {
	//log.Debugf("Loading config file: %s", path)

	file, e := ioutil.ReadFile(path)
	if e != nil {
		return fmt.Errorf("Failed to load file: %s error: %s", path, e)
	}

	content := make(map[string]interface{})
	e = json.Unmarshal(file, &content)
	if e != nil {
		return fmt.Errorf("Failed to read file: %s error: %s", path, e)
	}

	flatten(content, nil, config)
	return nil
}

func flatten(input interface{}, lpath []string, flattened map[string]interface{}) {
	if lpath == nil {
		lpath = []string{}
	}

	if reflect.ValueOf(input).Kind() == reflect.Map {
		for rkey, value := range input.(map[string]interface{}) {
			flatten(value, append(lpath, rkey), flattened)
		}
	} else {
		if _, ok := flattened[strings.Join(lpath, ".")]; !ok {
			flattened[strings.Join(lpath, ".")] = input
		}
	}
}

func unflatten(flat map[string]interface{}) map[string]interface{} {
	out := make(map[string]interface{})
	obj := out

	for key, val := range flat {

		obj = out
		keys := strings.Split(key, ".")

		for i, k := range keys {
			if i == len(keys)-1 {
				obj[k] = val
			} else {
				next, ok := obj[k]
				if !ok {
					next = make(map[string]interface{})
				}
				obj[k] = next
				obj = next.(map[string]interface{})
			}
		}

	}

	return out
}

func parseNumbers(config map[string]interface{}) {
	for name, val := range config {
		stringVal, ok := val.(string)
		if ok {
			floatVal, err := strconv.ParseFloat(stringVal, 64)
			if err == nil {
				config[name] = floatVal
			}
		}
	}
}

func getUserHome() string {
	usr, err := user.Current()
	if err != nil {
		return "/root"
	}
	return usr.HomeDir
}
<|endoftext|>


<commit_before>// Copyright 2014-2015 The DevMine 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 config takes care of the configuration file parsing.
package config

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"strings"
	"time"
)

// sslModes corresponds to the SSL modes available for the connection to the
// PostgreSQL database.
// See http://www.postgresql.org/docs/9.4/static/libpq-ssl.html for details.
var sslModes = map[string]bool{
	"disable":     true,
	"require":     true,
	"verify-ca":   true,
	"verify-full": true,
}

// Config is the main configuration structure.
type Config struct {
	// CloneDir is the path to the folder where all repositories are cloned.
	CloneDir string `json:"clone_dir"`

	// Crawlers is a group of crawlers configuration.
	Crawlers []CrawlerConfig `json:"crawlers"`

	// CrawlingTimeInterval corresponds to the time to wait between 2 full
	// crawling periods.
	CrawlingTimeInterval string `json:"crawling_time_interval"`

	// FetchTimeInterval corresponds to the time to wait betweeb 2 full
	// repositories fetching periods.
	FetchTimeInterval string `json:"fetch_time_interval"`

	// Database is the database configuration.
	Database DatabaseConfig `json:"database"`
}

// CrawlerConfig is a configuration for a crawler.
type CrawlerConfig struct {
	// Type defines the crawler type (eg: "github").
	Type string `json:"type"`

	// Languages is the list of programming languages of interest.
	Languages []string `json:"languages"`

	// Limit limits the number of repositories to crawl. Set this value to 0 to
	// not use a limit. Otherwise, crawling will stop when "limit" repositories
	// have been fetched.
	// Note that the behavior is slightly different whether UseSearchAPI is set
	// to true or not. When using the search API, this limit correspond to the
	// number of repositories to crawl per language listed in "languages".
	// Otherwhise, this is a global limit, regardless of the language.
	Limit int64 `json:"limit"`

	// SinceID corresponds to the repository ID (eg: GitHub repository ID in
	// the case of the github crawler) from which to start querying repositories.
	// Note that this value is ignored when using the search API.
	SinceID int `json:"since_id"`

	// Fork indicate whether "fork" repositories need to be crawled or not.
	Fork bool `json:"fork"`

	// OAuthAccessToken is the API token. If not provided, crawld will work but
	// the number of API call is usually limited to a low number.
	// For instance, in the case of the GitHub crawler, unauthenticated
	// requests are limited to 60 per hour where authenticated requests goes up
	// to 5000 per hour.
	OAuthAccessToken string `json:"oauth_access_token"`
	UseSearchAPI     bool   `json:"use_search_api"`
}

// DatabaseConfig is a configuration for PostgreSQL database connection
// information
type DatabaseConfig struct {
	// HostName is the hostname, or IP address, of the database server.
	HostName string `json:"hostname"`

	// Port is the PostgreSQL port.
	Port uint `json:"port"`

	// UserName is the PostgreSQL user that has access to the database.
	UserName string `json:"username"`

	// Password is the password of the database user.
	Password string `json:"password"`

	// DBName is the database name.
	DBName string `json:"dbname"`

	// SSLMode defines the SSL mode for the connection to the database.
	// Refer to sslModes for the possible values and their meaning.
	SSLMode string `json:"ssl_mode"`
}

// ReadConfig reads a JSON formatted configuration file, verifies the values
// of the configuration parameters and fills the Config structure.
func ReadConfig(path string) (*Config, error) {
	// TODO maybe use a safer function like io.Copy
	bs, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}

	cfg := new(Config)
	if err := json.Unmarshal(bs, cfg); err != nil {
		return nil, err
	}

	if err := cfg.verify(); err != nil {
		return nil, err
	}

	return cfg, nil
}

func (c Config) verify() error {
	if len(strings.Trim(c.CloneDir, " ")) == 0 {
		return errors.New("clone_dir cannot be empty")
	}

	if _, err := time.ParseDuration(c.CrawlingTimeInterval); err != nil {
		return errors.New("config: invalid crawling time interval format")
	}

	if _, err := time.ParseDuration(c.FetchTimeInterval); err != nil {
		return errors.New("config: invalid fetch time interval format")
	}

	for _, cs := range c.Crawlers {
		if err := cs.verify(); err != nil {
			return err
		}
	}

	if err := c.Database.verify(); err != nil {
		return err
	}

	return nil
}

func (cc CrawlerConfig) verify() error {
	if len(strings.Trim(cc.Type, " ")) == 0 {
		return errors.New("config: crawler type cannot be empty")
	}

	if len(cc.Languages) == 0 {
		return errors.New("config: crawler must have at least one language")
	}

	if cc.SinceID < 0 {
		return errors.New("config: crawler since id must be >= 0")
	}

	return nil
}

func (dc DatabaseConfig) verify() error {
	if len(strings.Trim(dc.HostName, " ")) == 0 {
		return errors.New("config: database hostname cannot be empty")
	}

	if dc.Port <= 0 {
		return errors.New("config: database port must be greater than 0")
	}

	if len(strings.Trim(dc.UserName, " ")) == 0 {
		return errors.New("config: database username cannot be empty")
	}

	if len(strings.Trim(dc.DBName, " ")) == 0 {
		return errors.New("config: database name cannot be empty")
	}

	if _, ok := sslModes[dc.SSLMode]; !ok {
		return errors.New("config: database can only be disable, require, verify-ca or verify-full")
	}

	return nil
}
<commit_msg>doc: Document undocumented field in crawler config.<commit_after>// Copyright 2014-2015 The DevMine 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 config takes care of the configuration file parsing.
package config

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"strings"
	"time"
)

// sslModes corresponds to the SSL modes available for the connection to the
// PostgreSQL database.
// See http://www.postgresql.org/docs/9.4/static/libpq-ssl.html for details.
var sslModes = map[string]bool{
	"disable":     true,
	"require":     true,
	"verify-ca":   true,
	"verify-full": true,
}

// Config is the main configuration structure.
type Config struct {
	// CloneDir is the path to the folder where all repositories are cloned.
	CloneDir string `json:"clone_dir"`

	// Crawlers is a group of crawlers configuration.
	Crawlers []CrawlerConfig `json:"crawlers"`

	// CrawlingTimeInterval corresponds to the time to wait between 2 full
	// crawling periods.
	CrawlingTimeInterval string `json:"crawling_time_interval"`

	// FetchTimeInterval corresponds to the time to wait betweeb 2 full
	// repositories fetching periods.
	FetchTimeInterval string `json:"fetch_time_interval"`

	// Database is the database configuration.
	Database DatabaseConfig `json:"database"`
}

// CrawlerConfig is a configuration for a crawler.
type CrawlerConfig struct {
	// Type defines the crawler type (eg: "github").
	Type string `json:"type"`

	// Languages is the list of programming languages of interest.
	Languages []string `json:"languages"`

	// Limit limits the number of repositories to crawl. Set this value to 0 to
	// not use a limit. Otherwise, crawling will stop when "limit" repositories
	// have been fetched.
	// Note that the behavior is slightly different whether UseSearchAPI is set
	// to true or not. When using the search API, this limit correspond to the
	// number of repositories to crawl per language listed in "languages".
	// Otherwise, this is a global limit, regardless of the language.
	Limit int64 `json:"limit"`

	// SinceID corresponds to the repository ID (eg: GitHub repository ID in
	// the case of the github crawler) from which to start querying repositories.
	// Note that this value is ignored when using the search API.
	SinceID int `json:"since_id"`

	// Fork indicate whether "fork" repositories need to be crawled or not.
	Fork bool `json:"fork"`

	// OAuthAccessToken is the API token. If not provided, crawld will work but
	// the number of API call is usually limited to a low number.
	// For instance, in the case of the GitHub crawler, unauthenticated
	// requests are limited to 60 per hour where authenticated requests goes up
	// to 5000 per hour.
	OAuthAccessToken string `json:"oauth_access_token"`

	// UseSearchAPI specifies whether to use the search API or not. The number
	// of results returned by a search API is usually limited. For instance,
	// the GitHub search API limits the results to 1000 repositories.
	// In the case of the github crawler, this means that the maximum number of
	// repositories that can be crawled is 1000 per language (the github crawler
	// orders the results by repository popularity with regard to the number of
	// stars). When a lot of data is wanted, this option shall therefore be set
	// to false.
	UseSearchAPI bool `json:"use_search_api"`
}

// DatabaseConfig is a configuration for PostgreSQL database connection
// information
type DatabaseConfig struct {
	// HostName is the hostname, or IP address, of the database server.
	HostName string `json:"hostname"`

	// Port is the PostgreSQL port.
	Port uint `json:"port"`

	// UserName is the PostgreSQL user that has access to the database.
	UserName string `json:"username"`

	// Password is the password of the database user.
	Password string `json:"password"`

	// DBName is the database name.
	DBName string `json:"dbname"`

	// SSLMode defines the SSL mode for the connection to the database.
	// Refer to sslModes for the possible values and their meaning.
	SSLMode string `json:"ssl_mode"`
}

// ReadConfig reads a JSON formatted configuration file, verifies the values
// of the configuration parameters and fills the Config structure.
func ReadConfig(path string) (*Config, error) {
	// TODO maybe use a safer function like io.Copy
	bs, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}

	cfg := new(Config)
	if err := json.Unmarshal(bs, cfg); err != nil {
		return nil, err
	}

	if err := cfg.verify(); err != nil {
		return nil, err
	}

	return cfg, nil
}

func (c Config) verify() error {
	if len(strings.Trim(c.CloneDir, " ")) == 0 {
		return errors.New("clone_dir cannot be empty")
	}

	if _, err := time.ParseDuration(c.CrawlingTimeInterval); err != nil {
		return errors.New("config: invalid crawling time interval format")
	}

	if _, err := time.ParseDuration(c.FetchTimeInterval); err != nil {
		return errors.New("config: invalid fetch time interval format")
	}

	for _, cs := range c.Crawlers {
		if err := cs.verify(); err != nil {
			return err
		}
	}

	if err := c.Database.verify(); err != nil {
		return err
	}

	return nil
}

func (cc CrawlerConfig) verify() error {
	if len(strings.Trim(cc.Type, " ")) == 0 {
		return errors.New("config: crawler type cannot be empty")
	}

	if len(cc.Languages) == 0 {
		return errors.New("config: crawler must have at least one language")
	}

	if cc.SinceID < 0 {
		return errors.New("config: crawler since id must be >= 0")
	}

	return nil
}

func (dc DatabaseConfig) verify() error {
	if len(strings.Trim(dc.HostName, " ")) == 0 {
		return errors.New("config: database hostname cannot be empty")
	}

	if dc.Port <= 0 {
		return errors.New("config: database port must be greater than 0")
	}

	if len(strings.Trim(dc.UserName, " ")) == 0 {
		return errors.New("config: database username cannot be empty")
	}

	if len(strings.Trim(dc.DBName, " ")) == 0 {
		return errors.New("config: database name cannot be empty")
	}

	if _, ok := sslModes[dc.SSLMode]; !ok {
		return errors.New("config: database can only be disable, require, verify-ca or verify-full")
	}

	return nil
}
<|endoftext|>


<commit_before>package config

import (
	"flag"
	"strings"
	"time"
)

// Config handles marshalling user supplied configuration data
type Config struct {
	InterfaceName string
	PrivateIP     string
	PublicIP      string
	SubnetMask    string

	ListenAddress string
	ListenPort    int

	Prefix       string
	LeaseTime    time.Duration
	SyncInterval time.Duration
	Retries      time.Duration
	EnableCrypto bool

	TLSCert string
	TLSKey  string
	TLSCA   string

	Datastore string
	Endpoints []string
	Username  string
	Password  string
}

// New generates a new config object
func New() *Config {
	ifaceName := flag.String("interface-name", "quantum", "The name for the TUN interface that will be used for forwarding. Use %d to have the OS pick an available interface name.")
	privateIP := flag.String("private-ip", "", "The private ip address of this node.")
	publicIP := flag.String("public-ip", "", "The public ip address of this node.")
	subnetMask := flag.String("subnet-mask", "16", "The subnet mask in bit width format")

	laddr := flag.String("listen-address", "0.0.0.0", "The ip address to listen on for forwarded packets.")
	lport := flag.Int("listen-port", 1099, "The ip port to listen on for forwarded packets.")

	prefix := flag.String("prefix", "quantum", "The etcd key that quantum information is stored under.")
	leaseTime := flag.Duration("lease-time", 300, "Lease time for the private ip address.")
	syncInterval := flag.Duration("sync-interval", 30, "The backend sync interval")
	retries := flag.Duration("retries", 5, "The number of times to retry aquiring the private ip address lease.")
	crypto := flag.Bool("crypto", true, "Whether or not to encrypt data sent and recieved, by this node, to and from the rest of the cluster.")

	tlsCert := flag.String("tls-cert", "", "The client certificate to use for authentication with the backend datastore.")
	tlsKey := flag.String("tls-key", "", "The client key to use for authentication with the backend datastore.")
	tlsCA := flag.String("tls-ca-cert", "", "The CA certificate to authenticate the backend datastore.")

	datastore := flag.String("datastore", "etcd", "The datastore backend to use, either consul or etcd")
	endpoints := flag.String("endpoints", "127.0.0.1:2379", "The datastore endpoints to use, in a comma separated list.")
	username := flag.String("username", "", "The datastore username to use for authentication.")
	password := flag.String("password", "", "The datastore password to use for authentication.")

	flag.Parse()

	parsedEndpoints := strings.Split(*endpoints, ",")
	return &Config{
		InterfaceName: *ifaceName,
		PrivateIP:     *privateIP,
		PublicIP:      *publicIP,
		SubnetMask:    *subnetMask,
		ListenAddress: *laddr,
		ListenPort:    *lport,
		Prefix:        *prefix,
		LeaseTime:     *leaseTime,
		SyncInterval:  *syncInterval,
		Retries:       *retries,
		EnableCrypto:  *crypto,
		TLSCert:       *tlsCert,
		TLSKey:        *tlsKey,
		TLSCA:         *tlsCA,
		Datastore:     *datastore,
		Endpoints:     parsedEndpoints,
		Username:      *username,
		Password:      *password,
	}
}
<commit_msg>Updated the config module to support dhcp<commit_after>package config

import (
	"flag"
	"github.com/Supernomad/quantum/ecdh"
	"strconv"
	"strings"
	"time"
)

// Config handles marshalling user supplied configuration data
type Config struct {
	InterfaceName string

	PrivateIP string
	PublicIP  string

	PublicAddress string

	PrivateKey []byte
	PublicKey  []byte

	ListenAddress string
	ListenPort    int

	Prefix string

	LeaseTime       time.Duration
	SyncInterval    time.Duration
	RefreshInterval time.Duration

	TLSEnabled bool
	TLSCert    string
	TLSKey     string
	TLSCA      string

	Datastore string
	Endpoints []string
	Username  string
	Password  string
}

// New generates a new config object
func New() *Config {
	ifaceName := flag.String("interface-name", "quantum%d", "The name for the TUN interface that will be used for forwarding. Use %d to have the OS pick an available interface name.")

	privateIP := flag.String("private-ip", "", "The private ip address of this node.")
	publicIP := flag.String("public-ip", "", "The public ip address of this node.")

	laddr := flag.String("listen-address", "0.0.0.0", "The ip address to listen on for forwarded packets.")
	lport := flag.Int("listen-port", 1099, "The ip port to listen on for forwarded packets.")

	prefix := flag.String("prefix", "quantum", "The etcd key that quantum information is stored under.")

	leaseTime := flag.Duration("lease-time", 300, "Lease time for the private ip address.")
	syncInterval := flag.Duration("sync-interval", 30, "The backend sync interval")
	refreshInterval := flag.Duration("refresh-interval", 60, "The backend lease refresh interval.")

	tlsCert := flag.String("tls-cert", "", "The client certificate to use for authentication with the backend datastore.")
	tlsKey := flag.String("tls-key", "", "The client key to use for authentication with the backend datastore.")
	tlsCA := flag.String("tls-ca-cert", "", "The CA certificate to authenticate the backend datastore.")

	datastore := flag.String("datastore", "etcd", "The datastore backend to use, either consul or etcd")
	endpoints := flag.String("endpoints", "127.0.0.1:2379", "The datastore endpoints to use, in a comma separated list.")
	username := flag.String("username", "", "The datastore username to use for authentication.")
	password := flag.String("password", "", "The datastore password to use for authentication.")

	flag.Parse()

	parsedEndpoints := strings.Split(*endpoints, ",")
	pubkey, privkey := ecdh.GenerateECKeyPair()

	tlsEnabled := false
	if *tlsCert != "" || *tlsKey != "" || *tlsCA != "" {
		tlsEnabled = true
	}

	return &Config{
		InterfaceName:   *ifaceName,
		PrivateIP:       *privateIP,
		PublicIP:        *publicIP,
		PublicAddress:   *publicIP + ":" + strconv.Itoa(*lport),
		PrivateKey:      privkey,
		PublicKey:       pubkey,
		ListenAddress:   *laddr,
		ListenPort:      *lport,
		Prefix:          *prefix,
		LeaseTime:       *leaseTime,
		SyncInterval:    *syncInterval,
		RefreshInterval: *refreshInterval,
		TLSCert:         *tlsCert,
		TLSKey:          *tlsKey,
		TLSCA:           *tlsCA,
		TLSEnabled:      tlsEnabled,
		Datastore:       *datastore,
		Endpoints:       parsedEndpoints,
		Username:        *username,
		Password:        *password,
	}
}
<|endoftext|>


<commit_before>/*
Package config helps creating configuration structs.

The intended usage would be a simple struct that calls DefaultValue() on
a fields initialization. E.g.

  var portProperty = config.NewEnvProperty("PORT", "8080")

  type Config struct {
    Port  string
  }

  func NewConfig() *Config {
    return &Config {
      Port: portProperty.DefaultValue()
    }
  }
*/
package config

import (
	"log"
	"os"
)

// Property can be used to define configuration properties with default values.
type Property interface {
	DefaultValue() string
}

// NewRequiredEnvProperty returns a Property that gets its default value from
// the specified environment variable. Panics if the variable is not set.
func NewRequiredEnvProperty(envVariableName string) Property {
	return requiredEnvProperty{
		envVariableName: envVariableName,
	}
}

// NewEnvProperty returns a Property that gets its default value from the
// specified environment variable. If the environment vatiable is not set
// the fallback value will be set as default instead
func NewEnvProperty(envVariableName string, fallbackValue string) Property {
	return envProperty{
		envVariableName: envVariableName,
		fallbackValue:   fallbackValue,
	}
}

type envProperty struct {
	envVariableName string
	fallbackValue   string
}

func (prop envProperty) DefaultValue() (defaultValue string) {
	defaultValue = os.Getenv(prop.envVariableName)
	if defaultValue == "" {
		defaultValue = prop.fallbackValue
	}
	return
}

type requiredEnvProperty struct {
	envVariableName string
}

func (prop requiredEnvProperty) DefaultValue() (defaultValue string) {
	defaultValue = os.Getenv(prop.envVariableName)
	if defaultValue == "" {
		log.Fatalf("Please set the %s environment variable", prop.envVariableName)
	}
	return
}
<commit_msg>update config package documentation & method naming<commit_after>/*
Package config helps creating configuration structs.

The intended usage would be a simple struct that calls Value() on
a fields initialization. E.g.

	var portProperty   = config.NewEnvProperty("PORT", "8080")
	// If the $DOMAIN env variable not set the configuration creation will fail with a fatal error
	var domainProperty = config.NewRequiredEnvProperty("DOMAIN")

	type Config struct {
		Port   string
		Domain string
	}

	func NewConfig() Config {
		return Config{
			Port:   portProperty.Value(),
			Domain: domainProperty.Value(),
		}
	}
*/
package config

import (
	"log"
	"os"
)

// Property can be used to fetch default values for configuration properties.
type Property interface {
	Value() string
}

// NewRequiredEnvProperty returns a Property that gets its value from
// the specified environment variable. Panics if the variable is not set.
func NewRequiredEnvProperty(envVariableName string) Property {
	return requiredEnvProperty{
		envVariableName: envVariableName,
	}
}

// NewEnvProperty returns a Property that gets its value from the
// specified environment variable. If the environment vatiable is not set
// the fallback value will be used instead
func NewEnvProperty(envVariableName string, fallbackValue string) Property {
	return envProperty{
		envVariableName: envVariableName,
		fallbackValue:   fallbackValue,
	}
}

type envProperty struct {
	envVariableName string
	fallbackValue   string
}

func (prop envProperty) Value() (defaultValue string) {
	defaultValue = os.Getenv(prop.envVariableName)
	if defaultValue == "" {
		defaultValue = prop.fallbackValue
	}
	return
}

type requiredEnvProperty struct {
	envVariableName string
}

func (prop requiredEnvProperty) Value() (defaultValue string) {
	defaultValue = os.Getenv(prop.envVariableName)
	if defaultValue == "" {
		log.Fatalf("Please set the %s environment variable", prop.envVariableName)
	}
	return
}
<|endoftext|>