<commit_before>/*
 * Copyright 2018 Google Inc. All rights reserved.
 *
 * 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 config

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"testing"

	"github.com/golang/protobuf/jsonpb"
	"github.com/golang/protobuf/proto"
	"github.com/google/go-cmp/cmp"

	ecpb "kythe.io/kythe/proto/extraction_config_go_proto"
)

const testDataDir = "testdata"

var multipleNewLines = regexp.MustCompile("\n{2,}")

func imagesEqual(got, want []byte) (bool, string) {
	// remove superfluous whitespace
	gotStr := strings.Trim(string(got[:]), " \n")
	wantStr := strings.Trim(string(want[:]), " \n")
	gotStr = multipleNewLines.ReplaceAllString(gotStr, "\n")
	wantStr = multipleNewLines.ReplaceAllString(wantStr, "\n")

	// diff want vs got
	diff := cmp.Diff(gotStr, wantStr)
	if diff != "" {
		return false, diff
	}

	return true, ""
}

func mustLoadDockerFile(t *testing.T, testConfigFile string) []byte {
	t.Helper()
	fileName := fmt.Sprintf("expected_%s.Dockerfile", strings.Replace(filepath.Base(testConfigFile), ".json", "", 1))
	content, err := ioutil.ReadFile(os.ExpandEnv(filepath.Join(testDataDir, fileName)))
	if err != nil {
		t.Fatalf("Failed to open test docker file: %v\n", err)
	}

	return content
}

type testFiles []*os.File

func (t testFiles) Close() error {
	var err error
	for _, f := range t {
		if cerr := f.Close(); err == nil {
			err = cerr
		}
	}
	return err
}

func mustOpenTestData(t *testing.T) testFiles {
	t.Helper()
	fileNames, err := filepath.Glob(os.ExpandEnv(fmt.Sprintf("%s/%s", testDataDir, "*.json")))
	if err != nil {
		t.Fatalf("Failed to glob for test data files: %v\n", err)
	}

	if len(fileNames) == 0 {
		t.Fatal("No test config data found!\n")
	}

	var files []*os.File
	for _, fileName := range fileNames {
		file, err := os.Open(os.ExpandEnv(fileName))
		if err != nil {
			t.Fatalf("Failed to load test data: %v", err)
		}

		files = append(files, file)
	}

	return files
}

func TestNewImageGeneratesExpectedDockerFiles(t *testing.T) {
	testData := mustOpenTestData(t)
	defer testData.Close()
	for _, file := range testData {
		config, err := load(file)
		if err != nil {
			t.Fatalf("Failed to load extraction config: %v", err)
		}

		got, err := newImage(config, imageSettings{})
		if err != nil {
			t.Fatalf("Failed to parse test config: %v\n", err)
		}

		want := mustLoadDockerFile(t, file.Name())
		if eq, diff := imagesEqual(want, got); !eq {

			t.Fatalf("Images were not equal, diff:\n%s", diff)
		}
	}
}

func TestLoadReturnsProperData(t *testing.T) {
	testData := mustOpenTestData(t)
	defer testData.Close()
	for _, file := range testData {
		got, err := load(file)
		if err != nil {
			t.Fatalf("Failed to load extraction config: %v", err)
		}

		_, err = file.Seek(0, io.SeekStart)
		if err != nil {
			t.Fatalf("Failed to seek test data: %v", err)
		}
		want := &ecpb.ExtractionConfiguration{}
		if err := jsonpb.Unmarshal(file, want); err != nil {
			t.Fatalf("Failed to unmarshal test data: %v", err)
		}

		if !proto.Equal(got, want) {
			t.Fatalf("Expected: %v\nGot: %v", want, got)
		}
	}
}

func TestcreateImageWritesProperData(t *testing.T) {
	tempDir, err := ioutil.TempDir("", "tempOutputDir")
	if err != nil {
		t.Fatalf("Failed to create temp dir: %v", err)
	}
	defer os.RemoveAll(tempDir)

	testData := mustOpenTestData(t)
	defer testData.Close()
	for _, file := range testData {
		config, err := load(file)
		if err != nil {
			t.Fatalf("Failed to load extraction config: %v", err)
		}

		tmpImageFile, err := ioutil.TempFile(tempDir, "tmpExtractionImage.Docker")
		if err != nil {
			t.Fatalf("Failed to create temp image file: %v", err)
		}

		err = createImage(config, imageSettings{}, tmpImageFile.Name())
		if err != nil {
			t.Fatalf("Failed to create image: %v", err)
		}

		got, err := ioutil.ReadAll(tmpImageFile)
		if err != nil {
			t.Fatalf("Failed to read created image: %v", err)
		}

		want := mustLoadDockerFile(t, file.Name())
		if eq, diff := imagesEqual(got, want); !eq {
			t.Fatalf("Images were not equal, diff:\n%s\n", diff)
		}
	}
}
<commit_msg>Fix a go "lint"-like error from a bad renaming. (#171)<commit_after>/*
 * Copyright 2018 Google Inc. All rights reserved.
 *
 * 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 config

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"testing"

	"github.com/golang/protobuf/jsonpb"
	"github.com/golang/protobuf/proto"
	"github.com/google/go-cmp/cmp"

	ecpb "kythe.io/kythe/proto/extraction_config_go_proto"
)

const testDataDir = "testdata"

var multipleNewLines = regexp.MustCompile("\n{2,}")

func imagesEqual(got, want []byte) (bool, string) {
	// remove superfluous whitespace
	gotStr := strings.Trim(string(got[:]), " \n")
	wantStr := strings.Trim(string(want[:]), " \n")
	gotStr = multipleNewLines.ReplaceAllString(gotStr, "\n")
	wantStr = multipleNewLines.ReplaceAllString(wantStr, "\n")

	// diff want vs got
	diff := cmp.Diff(gotStr, wantStr)
	if diff != "" {
		return false, diff
	}

	return true, ""
}

func mustLoadDockerFile(t *testing.T, testConfigFile string) []byte {
	t.Helper()
	fileName := fmt.Sprintf("expected_%s.Dockerfile", strings.Replace(filepath.Base(testConfigFile), ".json", "", 1))
	content, err := ioutil.ReadFile(os.ExpandEnv(filepath.Join(testDataDir, fileName)))
	if err != nil {
		t.Fatalf("Failed to open test docker file: %v\n", err)
	}

	return content
}

type testFiles []*os.File

func (t testFiles) Close() error {
	var err error
	for _, f := range t {
		if cerr := f.Close(); err == nil {
			err = cerr
		}
	}
	return err
}

func mustOpenTestData(t *testing.T) testFiles {
	t.Helper()
	fileNames, err := filepath.Glob(os.ExpandEnv(fmt.Sprintf("%s/%s", testDataDir, "*.json")))
	if err != nil {
		t.Fatalf("Failed to glob for test data files: %v\n", err)
	}

	if len(fileNames) == 0 {
		t.Fatal("No test config data found!\n")
	}

	var files []*os.File
	for _, fileName := range fileNames {
		file, err := os.Open(os.ExpandEnv(fileName))
		if err != nil {
			t.Fatalf("Failed to load test data: %v", err)
		}

		files = append(files, file)
	}

	return files
}

func TestNewImageGeneratesExpectedDockerFiles(t *testing.T) {
	testData := mustOpenTestData(t)
	defer testData.Close()
	for _, file := range testData {
		config, err := load(file)
		if err != nil {
			t.Fatalf("Failed to load extraction config: %v", err)
		}

		got, err := newImage(config, imageSettings{})
		if err != nil {
			t.Fatalf("Failed to parse test config: %v\n", err)
		}

		want := mustLoadDockerFile(t, file.Name())
		if eq, diff := imagesEqual(want, got); !eq {

			t.Fatalf("Images were not equal, diff:\n%s", diff)
		}
	}
}

func TestLoadReturnsProperData(t *testing.T) {
	testData := mustOpenTestData(t)
	defer testData.Close()
	for _, file := range testData {
		got, err := load(file)
		if err != nil {
			t.Fatalf("Failed to load extraction config: %v", err)
		}

		_, err = file.Seek(0, io.SeekStart)
		if err != nil {
			t.Fatalf("Failed to seek test data: %v", err)
		}
		want := &ecpb.ExtractionConfiguration{}
		if err := jsonpb.Unmarshal(file, want); err != nil {
			t.Fatalf("Failed to unmarshal test data: %v", err)
		}

		if !proto.Equal(got, want) {
			t.Fatalf("Expected: %v\nGot: %v", want, got)
		}
	}
}

func TestCreateImageWritesProperData(t *testing.T) {
	tempDir, err := ioutil.TempDir("", "tempOutputDir")
	if err != nil {
		t.Fatalf("Failed to create temp dir: %v", err)
	}
	defer os.RemoveAll(tempDir)

	testData := mustOpenTestData(t)
	defer testData.Close()
	for _, file := range testData {
		config, err := load(file)
		if err != nil {
			t.Fatalf("Failed to load extraction config: %v", err)
		}

		tmpImageFile, err := ioutil.TempFile(tempDir, "tmpExtractionImage.Docker")
		if err != nil {
			t.Fatalf("Failed to create temp image file: %v", err)
		}

		err = createImage(config, imageSettings{}, tmpImageFile.Name())
		if err != nil {
			t.Fatalf("Failed to create image: %v", err)
		}

		got, err := ioutil.ReadAll(tmpImageFile)
		if err != nil {
			t.Fatalf("Failed to read created image: %v", err)
		}

		want := mustLoadDockerFile(t, file.Name())
		if eq, diff := imagesEqual(got, want); !eq {
			t.Fatalf("Images were not equal, diff:\n%s\n", diff)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2012 Jesse van den Kieboom. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
Package flags provides an extensive command line option parser.
The flags package is similar in functionality to the go built-in flag package
but provides more options and uses reflection to provide a convenient and
succinct way of specifying command line options.

Supported features

The following features are supported in go-flags:

    Options with short names (-v)
    Options with long names (--verbose)
    Options with and without arguments (bool v.s. other type)
    Options with optional arguments and default values
    Option default values from ENVIRONMENT_VARIABLES, including slice and map values
    Multiple option groups each containing a set of options
    Generate and print well-formatted help message
    Passing remaining command line arguments after -- (optional)
    Ignoring unknown command line options (optional)
    Supports -I/usr/include -I=/usr/include -I /usr/include option argument specification
    Supports multiple short options -aux
    Supports all primitive go types (string, int{8..64}, uint{8..64}, float)
    Supports same option multiple times (can store in slice or last option counts)
    Supports maps
    Supports function callbacks
    Supports namespaces for (nested) option groups

Additional features specific to Windows:
    Options with short names (/v)
    Options with long names (/verbose)
    Windows-style options with arguments use a colon as the delimiter
    Modify generated help message with Windows-style / options

Basic usage

The flags package uses structs, reflection and struct field tags
to allow users to specify command line options. This results in very simple
and concise specification of your application options. For example:

    type Options struct {
        Verbose []bool `short:"v" long:"verbose" description:"Show verbose debug information"`
    }

This specifies one option with a short name -v and a long name --verbose.
When either -v or --verbose is found on the command line, a 'true' value
will be appended to the Verbose field. e.g. when specifying -vvv, the
resulting value of Verbose will be {[true, true, true]}.

Slice options work exactly the same as primitive type options, except that
whenever the option is encountered, a value is appended to the slice.

Map options from string to primitive type are also supported. On the command
line, you specify the value for such an option as key:value. For example

    type Options struct {
        AuthorInfo string[string] `short:"a"`
    }

Then, the AuthorInfo map can be filled with something like
-a name:Jesse -a "surname:van den Kieboom".

Finally, for full control over the conversion between command line argument
values and options, user defined types can choose to implement the Marshaler
and Unmarshaler interfaces.

Available field tags

The following is a list of tags for struct fields supported by go-flags:

    short:            the short name of the option (single character)
    long:             the long name of the option
    required:         whether an option is required to appear on the command
                      line. If a required option is not present, the parser will
                      return ErrRequired (optional)
    description:      the description of the option (optional)
    long-description: the long description of the option. Currently only
                      displayed in generated man pages (optional)
    no-flag:          if non-empty this field is ignored as an option (optional)

    optional:       whether an argument of the option is optional. When an
                    argument is optional it can only be specified using
                    --option=argument (optional)
    optional-value: the value of an optional option when the option occurs
                    without an argument. This tag can be specified multiple
                    times in the case of maps or slices (optional)
    default:        the default value of an option. This tag can be specified
                    multiple times in the case of slices or maps (optional)
    default-mask:   when specified, this value will be displayed in the help
                    instead of the actual default value. This is useful
                    mostly for hiding otherwise sensitive information from
                    showing up in the help. If default-mask takes the special
                    value "-", then no default value will be shown at all
                    (optional)
    env:            the default value of the option is overridden from the
                    specified environment variable, if one has been defined.
                    (optional)
    env-delim:      the 'env' default value from environment is split into
                    multiple values with the given delimiter string, use with
                    slices and maps (optional)
    value-name:     the name of the argument value (to be shown in the help)
                    (optional)
    choice:         limits the values for an option to a set of values.
                    This tag can be specified mltiple times (optional)
    hidden:         the option is not visible in the help or man page.

    base: a base (radix) used to convert strings to integer values, the
          default base is 10 (i.e. decimal) (optional)

    ini-name:       the explicit ini option name (optional)
    no-ini:         if non-empty this field is ignored as an ini option
                    (optional)

    group:                when specified on a struct field, makes the struct
                          field a separate group with the given name (optional)
    namespace:            when specified on a group struct field, the namespace
                          gets prepended to every option's long name and
                          subgroup's namespace of this group, separated by
                          the parser's namespace delimiter (optional)
    command:              when specified on a struct field, makes the struct
                          field a (sub)command with the given name (optional)
    subcommands-optional: when specified on a command struct field, makes
                          any subcommands of that command optional (optional)
    alias:                when specified on a command struct field, adds the
                          specified name as an alias for the command. Can be
                          be specified multiple times to add more than one
                          alias (optional)
    positional-args:      when specified on a field with a struct type,
                          uses the fields of that struct to parse remaining
                          positional command line arguments into (in order
                          of the fields). If a field has a slice type,
                          then all remaining arguments will be added to it.
                          Positional arguments are optional by default,
                          unless the "required" tag is specified together
                          with the "positional-args" tag (optional)
    positional-arg-name:  used on a field in a positional argument struct; name
                          of the positional argument placeholder to be shown in
                          the help (optional)

Either the `short:` tag or the `long:` must be specified to make the field eligible as an
option.

Option groups

Option groups are a simple way to semantically separate your options. All
options in a particular group are shown together in the help under the name
of the group. Namespaces can be used to specify option long names more
precisely and emphasize the options affiliation to their group.

There are currently three ways to specify option groups.

    1. Use NewNamedParser specifying the various option groups.
    2. Use AddGroup to add a group to an existing parser.
    3. Add a struct field to the top-level options annotated with the
       group:"group-name" tag.

Commands

The flags package also has basic support for commands. Commands are often
used in monolithic applications that support various commands or actions.
Take git for example, all of the add, commit, checkout, etc. are called
commands. Using commands you can easily separate multiple functions of your
application.

There are currently two ways to specify a command.

    1. Use AddCommand on an existing parser.
    2. Add a struct field to your options struct annotated with the
       command:"command-name" tag.

The most common, idiomatic way to implement commands is to define a global
parser instance and implement each command in a separate file. These
command files should define a go init function which calls AddCommand on
the global parser.

When parsing ends and there is an active command and that command implements
the Commander interface, then its Execute method will be run with the
remaining command line arguments.

Command structs can have options which become valid to parse after the
command has been specified on the command line, in addition to the options
of all the parent commands. I.e. considering a -v flag on the parser and an
add command, the following are equivalent:

    ./app -v add
    ./app add -v

However, if the -v flag is defined on the add command, then the first of
the two examples above would fail since the -v flag is not defined before
the add command.

Completion

go-flags has builtin support to provide bash completion of flags, commands
and argument values. To use completion, the binary which uses go-flags
can be invoked in a special environment to list completion of the current
command line argument. It should be noted that this `executes` your application,
and it is up to the user to make sure there are no negative side effects (for
example from init functions).

Setting the environment variable `GO_FLAGS_COMPLETION=1` enables completion
by replacing the argument parsing routine with the completion routine which
outputs completions for the passed arguments. The basic invocation to
complete a set of arguments is therefore:

    GO_FLAGS_COMPLETION=1 ./completion-example arg1 arg2 arg3

where `completion-example` is the binary, `arg1` and `arg2` are
the current arguments, and `arg3` (the last argument) is the argument
to be completed. If the GO_FLAGS_COMPLETION is set to "verbose", then
descriptions of possible completion items will also be shown, if there
are more than 1 completion items.

To use this with bash completion, a simple file can be written which
calls the binary which supports go-flags completion:

    _completion_example() {
        # All arguments except the first one
        args=("${COMP_WORDS[@]:1:$COMP_CWORD}")

        # Only split on newlines
        local IFS=$'\n'

        # Call completion (note that the first element of COMP_WORDS is
        # the executable itself)
        COMPREPLY=($(GO_FLAGS_COMPLETION=1 ${COMP_WORDS[0]} "${args[@]}"))
        return 0
    }

    complete -F _completion_example completion-example

Completion requires the parser option PassDoubleDash and is therefore enforced if the environment variable GO_FLAGS_COMPLETION is set.

Customized completion for argument values is supported by implementing
the flags.Completer interface for the argument value type. An example
of a type which does so is the flags.Filename type, an alias of string
allowing simple filename completion. A slice or array argument value
whose element type implements flags.Completer will also be completed.
*/
package flags
<commit_msg>Add doc for required rest positional arguments<commit_after>// Copyright 2012 Jesse van den Kieboom. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
Package flags provides an extensive command line option parser.
The flags package is similar in functionality to the go built-in flag package
but provides more options and uses reflection to provide a convenient and
succinct way of specifying command line options.

Supported features

The following features are supported in go-flags:

    Options with short names (-v)
    Options with long names (--verbose)
    Options with and without arguments (bool v.s. other type)
    Options with optional arguments and default values
    Option default values from ENVIRONMENT_VARIABLES, including slice and map values
    Multiple option groups each containing a set of options
    Generate and print well-formatted help message
    Passing remaining command line arguments after -- (optional)
    Ignoring unknown command line options (optional)
    Supports -I/usr/include -I=/usr/include -I /usr/include option argument specification
    Supports multiple short options -aux
    Supports all primitive go types (string, int{8..64}, uint{8..64}, float)
    Supports same option multiple times (can store in slice or last option counts)
    Supports maps
    Supports function callbacks
    Supports namespaces for (nested) option groups

Additional features specific to Windows:
    Options with short names (/v)
    Options with long names (/verbose)
    Windows-style options with arguments use a colon as the delimiter
    Modify generated help message with Windows-style / options

Basic usage

The flags package uses structs, reflection and struct field tags
to allow users to specify command line options. This results in very simple
and concise specification of your application options. For example:

    type Options struct {
        Verbose []bool `short:"v" long:"verbose" description:"Show verbose debug information"`
    }

This specifies one option with a short name -v and a long name --verbose.
When either -v or --verbose is found on the command line, a 'true' value
will be appended to the Verbose field. e.g. when specifying -vvv, the
resulting value of Verbose will be {[true, true, true]}.

Slice options work exactly the same as primitive type options, except that
whenever the option is encountered, a value is appended to the slice.

Map options from string to primitive type are also supported. On the command
line, you specify the value for such an option as key:value. For example

    type Options struct {
        AuthorInfo string[string] `short:"a"`
    }

Then, the AuthorInfo map can be filled with something like
-a name:Jesse -a "surname:van den Kieboom".

Finally, for full control over the conversion between command line argument
values and options, user defined types can choose to implement the Marshaler
and Unmarshaler interfaces.

Available field tags

The following is a list of tags for struct fields supported by go-flags:

    short:            the short name of the option (single character)
    long:             the long name of the option
    required:         whether an option is required to appear on the command
                      line. If a required option is not present, the parser will
                      return ErrRequired (optional)
    description:      the description of the option (optional)
    long-description: the long description of the option. Currently only
                      displayed in generated man pages (optional)
    no-flag:          if non-empty this field is ignored as an option (optional)

    optional:       whether an argument of the option is optional. When an
                    argument is optional it can only be specified using
                    --option=argument (optional)
    optional-value: the value of an optional option when the option occurs
                    without an argument. This tag can be specified multiple
                    times in the case of maps or slices (optional)
    default:        the default value of an option. This tag can be specified
                    multiple times in the case of slices or maps (optional)
    default-mask:   when specified, this value will be displayed in the help
                    instead of the actual default value. This is useful
                    mostly for hiding otherwise sensitive information from
                    showing up in the help. If default-mask takes the special
                    value "-", then no default value will be shown at all
                    (optional)
    env:            the default value of the option is overridden from the
                    specified environment variable, if one has been defined.
                    (optional)
    env-delim:      the 'env' default value from environment is split into
                    multiple values with the given delimiter string, use with
                    slices and maps (optional)
    value-name:     the name of the argument value (to be shown in the help)
                    (optional)
    choice:         limits the values for an option to a set of values.
                    This tag can be specified mltiple times (optional)
    hidden:         the option is not visible in the help or man page.

    base: a base (radix) used to convert strings to integer values, the
          default base is 10 (i.e. decimal) (optional)

    ini-name:       the explicit ini option name (optional)
    no-ini:         if non-empty this field is ignored as an ini option
                    (optional)

    group:                when specified on a struct field, makes the struct
                          field a separate group with the given name (optional)
    namespace:            when specified on a group struct field, the namespace
                          gets prepended to every option's long name and
                          subgroup's namespace of this group, separated by
                          the parser's namespace delimiter (optional)
    command:              when specified on a struct field, makes the struct
                          field a (sub)command with the given name (optional)
    subcommands-optional: when specified on a command struct field, makes
                          any subcommands of that command optional (optional)
    alias:                when specified on a command struct field, adds the
                          specified name as an alias for the command. Can be
                          be specified multiple times to add more than one
                          alias (optional)
    positional-args:      when specified on a field with a struct type,
                          uses the fields of that struct to parse remaining
                          positional command line arguments into (in order
                          of the fields). If a field has a slice type,
                          then all remaining arguments will be added to it.
                          Positional arguments are optional by default,
                          unless the "required" tag is specified together
                          with the "positional-args" tag. The "required" tag
                          can also be set on the individual rest argument
                          fields, to require only the first N positional
                          arguments. If the "required" tag is set on the
                          rest arguments slice, then its value determines
                          the minimum amount of rest arguments that needs to
                          be provided (e.g. `required:"2"`) (optional)
    positional-arg-name:  used on a field in a positional argument struct; name
                          of the positional argument placeholder to be shown in
                          the help (optional)

Either the `short:` tag or the `long:` must be specified to make the field eligible as an
option.

Option groups

Option groups are a simple way to semantically separate your options. All
options in a particular group are shown together in the help under the name
of the group. Namespaces can be used to specify option long names more
precisely and emphasize the options affiliation to their group.

There are currently three ways to specify option groups.

    1. Use NewNamedParser specifying the various option groups.
    2. Use AddGroup to add a group to an existing parser.
    3. Add a struct field to the top-level options annotated with the
       group:"group-name" tag.

Commands

The flags package also has basic support for commands. Commands are often
used in monolithic applications that support various commands or actions.
Take git for example, all of the add, commit, checkout, etc. are called
commands. Using commands you can easily separate multiple functions of your
application.

There are currently two ways to specify a command.

    1. Use AddCommand on an existing parser.
    2. Add a struct field to your options struct annotated with the
       command:"command-name" tag.

The most common, idiomatic way to implement commands is to define a global
parser instance and implement each command in a separate file. These
command files should define a go init function which calls AddCommand on
the global parser.

When parsing ends and there is an active command and that command implements
the Commander interface, then its Execute method will be run with the
remaining command line arguments.

Command structs can have options which become valid to parse after the
command has been specified on the command line, in addition to the options
of all the parent commands. I.e. considering a -v flag on the parser and an
add command, the following are equivalent:

    ./app -v add
    ./app add -v

However, if the -v flag is defined on the add command, then the first of
the two examples above would fail since the -v flag is not defined before
the add command.

Completion

go-flags has builtin support to provide bash completion of flags, commands
and argument values. To use completion, the binary which uses go-flags
can be invoked in a special environment to list completion of the current
command line argument. It should be noted that this `executes` your application,
and it is up to the user to make sure there are no negative side effects (for
example from init functions).

Setting the environment variable `GO_FLAGS_COMPLETION=1` enables completion
by replacing the argument parsing routine with the completion routine which
outputs completions for the passed arguments. The basic invocation to
complete a set of arguments is therefore:

    GO_FLAGS_COMPLETION=1 ./completion-example arg1 arg2 arg3

where `completion-example` is the binary, `arg1` and `arg2` are
the current arguments, and `arg3` (the last argument) is the argument
to be completed. If the GO_FLAGS_COMPLETION is set to "verbose", then
descriptions of possible completion items will also be shown, if there
are more than 1 completion items.

To use this with bash completion, a simple file can be written which
calls the binary which supports go-flags completion:

    _completion_example() {
        # All arguments except the first one
        args=("${COMP_WORDS[@]:1:$COMP_CWORD}")

        # Only split on newlines
        local IFS=$'\n'

        # Call completion (note that the first element of COMP_WORDS is
        # the executable itself)
        COMPREPLY=($(GO_FLAGS_COMPLETION=1 ${COMP_WORDS[0]} "${args[@]}"))
        return 0
    }

    complete -F _completion_example completion-example

Completion requires the parser option PassDoubleDash and is therefore enforced if the environment variable GO_FLAGS_COMPLETION is set.

Customized completion for argument values is supported by implementing
the flags.Completer interface for the argument value type. An example
of a type which does so is the flags.Filename type, an alias of string
allowing simple filename completion. A slice or array argument value
whose element type implements flags.Completer will also be completed.
*/
package flags
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/codegangsta/cli"
	"github.com/fatih/color"
	"github.com/pengwynn/flint/flint"
	"os"
)

func main() {
	app := cli.NewApp()
	app.Name = "flint"
	app.Usage = "Check a project for common sources of contributor friction"
	app.Version = "0.0.3"
	app.Flags = []cli.Flag{
		cli.BoolFlag{"skip-readme", "skip check for README"},
		cli.BoolFlag{"skip-contributing", "skip check for contributing guide"},
		cli.BoolFlag{"skip-license", "skip check for license"},
		cli.BoolFlag{"skip-bootstrap", "skip check for bootstrap script"},
		cli.BoolFlag{"skip-test", "skip check for test script"},
		cli.BoolFlag{"skip-scripts", "skip check for all scripts"},
		cli.BoolFlag{"no-color", "skip coloring the terminal output"},
	}
	app.Action = func(c *cli.Context) {
		path, _ := os.Getwd()
		if len(c.Args()) > 0 {
			path = c.Args()[0]
		}
		linter := &flint.Lint{Path: path}

		if !c.Bool("skip-readme") {
			linter.CheckReadme()
		}
		if !c.Bool("skip-contributing") {
			linter.CheckContributing()
		}
		if !c.Bool("skip-license") {
			linter.CheckLicense()
		}
		if !c.Bool("skip-scripts") {
			if !c.Bool("skip-bootstrap") {
				linter.CheckBootstrap()
			}
			if !c.Bool("skip-test") {
				linter.CheckTest()
			}
		}

		if len(linter.Errors) > 0 {
			for _, element := range linter.Errors {
				if !c.Bool("no-color") { // if not skipping output color
					if element.Level == 0 { // if [FIXME]
						color.White(element.Message)
					} else { // if [ERROR]
						color.Yellow(element.Message)
					}
				} else {
					fmt.Println(element.Message)
				}
			}
			level := linter.Severity()
			if level > 0 {
				if !c.Bool("no-color") {
					color.Red("[CRITICAL] Some critical problems found. Please fix right away!")
				} else {
					fmt.Println("[CRITICAL] Some critical problems found. Please fix right away!")
				}
			}
			os.Exit(level)
		} else {
			if !c.Bool("no-color") {
				color.Green("[OK] All is well!")
			} else {
				fmt.Println("[OK] All is well!")
			}
		}
	}

	if err := app.Run(os.Args); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}
<commit_msg>Update flags parsing for cli package changes<commit_after>package main

import (
	"fmt"
	"os"

	"github.com/codegangsta/cli"
	"github.com/fatih/color"
	"github.com/pengwynn/flint/flint"
)

func main() {
	app := cli.NewApp()
	app.Name = "flint"
	app.Usage = "Check a project for common sources of contributor friction"
	app.Version = "0.0.3"
	app.Flags = []cli.Flag{
		cli.BoolFlag{"skip-readme", "skip check for README", ""},
		cli.BoolFlag{"skip-contributing", "skip check for contributing guide", ""},
		cli.BoolFlag{"skip-license", "skip check for license", ""},
		cli.BoolFlag{"skip-bootstrap", "skip check for bootstrap script", ""},
		cli.BoolFlag{"skip-test", "skip check for test script", ""},
		cli.BoolFlag{"skip-scripts", "skip check for all scripts", ""},
		cli.BoolFlag{"no-color", "skip coloring the terminal output", ""},
	}
	app.Action = func(c *cli.Context) {
		path, _ := os.Getwd()
		if len(c.Args()) > 0 {
			path = c.Args()[0]
		}
		linter := &flint.Lint{Path: path}

		if !c.Bool("skip-readme") {
			linter.CheckReadme()
		}
		if !c.Bool("skip-contributing") {
			linter.CheckContributing()
		}
		if !c.Bool("skip-license") {
			linter.CheckLicense()
		}
		if !c.Bool("skip-scripts") {
			if !c.Bool("skip-bootstrap") {
				linter.CheckBootstrap()
			}
			if !c.Bool("skip-test") {
				linter.CheckTest()
			}
		}

		if len(linter.Errors) > 0 {
			for _, element := range linter.Errors {
				if !c.Bool("no-color") { // if not skipping output color
					if element.Level == 0 { // if [FIXME]
						color.White(element.Message)
					} else { // if [ERROR]
						color.Yellow(element.Message)
					}
				} else {
					fmt.Println(element.Message)
				}
			}
			level := linter.Severity()
			if level > 0 {
				if !c.Bool("no-color") {
					color.Red("[CRITICAL] Some critical problems found. Please fix right away!")
				} else {
					fmt.Println("[CRITICAL] Some critical problems found. Please fix right away!")
				}
			}
			os.Exit(level)
		} else {
			if !c.Bool("no-color") {
				color.Green("[OK] All is well!")
			} else {
				fmt.Println("[OK] All is well!")
			}
		}
	}

	if err := app.Run(os.Args); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"

	"github.com/gorilla/mux"
	"github.com/streadway/amqp"
)

type Message struct {
	Duration int    `json:"duration"` // 1 to 60 validation
	Text     string `json:"text"`     //32 max validation
}

func main() {

	router := mux.NewRouter().StrictSlash(true)
	router.HandleFunc("/", Index)
	router.HandleFunc("/api/test", Test).Methods("GET")
	router.HandleFunc("/api/message", MessageFunc).Methods("POST")

	log.Fatal(http.ListenAndServe(":"+os.Args[1], router))
}

func Index(w http.ResponseWriter, r *http.Request) {
	filename := "index.html"
	body, _ := ioutil.ReadFile(filename)
	w.Header().Set("Content-Type", "text/html; charset=UTF-8")
	fmt.Fprintf(w, string(body))
}

func Test(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Working...")
}

func MessageFunc(w http.ResponseWriter, r *http.Request) {
	var priority = r.URL.Query().Get("priority")
	var item = new(Message)
	json.NewDecoder(r.Body).Decode(item)

	if priority == "" {
		priority = "false"
	}

	var isBool, err = strconv.ParseBool(priority)
	if err != nil {
		http.Error(w, "Invalid priority", 400)
		return
	}

	if item.Duration < 1 || item.Duration > 60 {
		http.Error(w, "Invalid duration", 400)
		return
	}

	if len(item.Text) > 32 {
		http.Error(w, "Invalid text", 400)
		return
	}

	if isBool {
		str, err := json.Marshal(item)
		if err != nil {
			http.Error(w, "Invalid item", 400)
			return
		}
		SendMessage(os.Args[2], "priority", str)
		w.WriteHeader(http.StatusOK)
		w.Write([]byte("OK Priority"))
	} else {
		str, err := json.Marshal(item)
		if err != nil {
			http.Error(w, "Invalid item", 400)
			return
		}
		SendMessage(os.Args[2], "messages", str)
		w.WriteHeader(http.StatusOK)
		w.Write([]byte("OK"))
	}

	return
}

func failOnError(err error, msg string) {
	if err != nil {
		log.Fatalf("%s: %s", msg, err)
	}
}

func SendMessage(amqpUrl string, queueName string, body []byte) {
	conn, err := amqp.Dial(amqpUrl)
	failOnError(err, "Failed to connect to RabbitMQ")
	defer conn.Close()

	ch, err := conn.Channel()
	failOnError(err, "Failed to open a channel")
	defer ch.Close()

	q, err := ch.QueueDeclare(
		queueName, // name
		true,      // durable
		false,     // delete when unused
		false,     // exclusive
		false,     // no-wait
		nil,       // arguments
	)
	failOnError(err, "Failed to declare a queue")

	err = ch.Publish(
		"",     // exchange
		q.Name, // routing key
		false,  // mandatory
		false,  // immediate
		amqp.Publishing{
			ContentType: "text/plain",
			Body:        body,
		})
	log.Printf(" [x] Sent %s", body)
	failOnError(err, "Failed to publish a message")
}
<commit_msg>Fixed args issue<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strconv"

	"github.com/gorilla/mux"
	"github.com/streadway/amqp"
)

type Message struct {
	Duration int    `json:"duration"` // 1 to 60 validation
	Text     string `json:"text"`     //32 max validation
}

func main() {

	router := mux.NewRouter().StrictSlash(true)
	router.HandleFunc("/", Index)
	router.HandleFunc("/api/test", Test)
	router.HandleFunc("/api/message", MessageFunc)
	log.Fatal(http.ListenAndServe(":5000", router))
}

func Index(w http.ResponseWriter, r *http.Request) {
	filename := "index.html"
	body, _ := ioutil.ReadFile(filename)
	w.Header().Set("Content-Type", "text/html; charset=UTF-8")
	fmt.Fprintf(w, string(body))
}

func Test(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Working...")
}

func MessageFunc(w http.ResponseWriter, r *http.Request) {
	var priority = r.URL.Query().Get("priority")
	var item = new(Message)
	json.NewDecoder(r.Body).Decode(item)

	if priority == "" {
		priority = "false"
	}

	var isBool, err = strconv.ParseBool(priority)
	if err != nil {
		http.Error(w, "Invalid priority", 400)
		return
	}

	if item.Duration < 1 || item.Duration > 60 {
		http.Error(w, "Invalid duration", 400)
		return
	}

	if len(item.Text) > 32 {
		http.Error(w, "Invalid text", 400)
		return
	}

	if isBool {
		str, err := json.Marshal(item)
		if err != nil {
			http.Error(w, "Invalid item", 400)
			return
		}
		SendMessage(os.Args[1], "priority", str)
		w.WriteHeader(http.StatusOK)
		w.Write([]byte("OK Priority"))
	} else {
		str, err := json.Marshal(item)
		if err != nil {
			http.Error(w, "Invalid item", 400)
			return
		}
		SendMessage(os.Args[1], "messages", str)
		w.WriteHeader(http.StatusOK)
		w.Write([]byte("OK"))
	}

	return
}

func failOnError(err error, msg string) {
	if err != nil {
		log.Fatalf("%s: %s", msg, err)
	}
}

func SendMessage(amqpUrl string, queueName string, body []byte) {
	conn, err := amqp.Dial(amqpUrl)
	failOnError(err, "Failed to connect to RabbitMQ")
	defer conn.Close()

	ch, err := conn.Channel()
	failOnError(err, "Failed to open a channel")
	defer ch.Close()

	q, err := ch.QueueDeclare(
		queueName, // name
		true,      // durable
		false,     // delete when unused
		false,     // exclusive
		false,     // no-wait
		nil,       // arguments
	)
	failOnError(err, "Failed to declare a queue")

	err = ch.Publish(
		"",     // exchange
		q.Name, // routing key
		false,  // mandatory
		false,  // immediate
		amqp.Publishing{
			ContentType: "text/plain",
			Body:        body,
		})
	log.Printf(" [x] Sent %s", body)
	failOnError(err, "Failed to publish a message")
}
<|endoftext|>


<commit_before>// Copyright 2016 Stratumn SAS. All rights reserved.
//
// 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 filestore implements a store that saves all the segments to the file system.
//
// The segments are stored as JSON files named after the link hashes.
// It's a convenient store to use during the development of an agent.
// However, because it doesn't use an index, it's very slow, and shouldn't be used for production.
package filestore

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"sort"

	"github.com/stratumn/go/cs"
	"github.com/stratumn/go/store"
	"github.com/stratumn/go/types"
)

const (
	// Name is the name set in the store's information.
	Name = "file"

	// Description is the description set in the store's information.
	Description = "Stratumn File Store"

	// DefaultPath is the path where segments will be saved by default.
	DefaultPath = "/var/filestore"
)

// FileStore is the type that implements github.com/stratumn/go/store.Adapter.
type FileStore struct {
	config *Config
}

// Config contains configuration options for the store.
type Config struct {
	// A version string that will be set in the store's information.
	Version string

	// A git commit hash that will be set in the store's information.
	Commit string

	// Path where segments will be saved.
	Path string
}

// Info is the info returned by GetInfo.
type Info struct {
	Name        string `json:"name"`
	Description string `json:"description"`
	Version     string `json:"version"`
	Commit      string `json:"commit"`
}

// New creates an instance of a FileStore.
func New(config *Config) *FileStore {
	return &FileStore{config}
}

// GetInfo implements github.com/stratumn/go/store.Adapter.GetInfo.
func (a *FileStore) GetInfo() (interface{}, error) {
	return &Info{
		Name:        Name,
		Description: Description,
		Version:     a.config.Version,
		Commit:      a.config.Commit,
	}, nil
}

// SaveSegment implements github.com/stratumn/go/store.Adapter.SaveSegment.
func (a *FileStore) SaveSegment(segment *cs.Segment) error {
	js, err := json.MarshalIndent(segment, "", "  ")
	if err != nil {
		return err
	}

	if err = os.MkdirAll(a.config.Path, 0755); err != nil {
		return err
	}

	segmentPath := path.Join(a.config.Path, segment.Meta["linkHash"].(string)+".json")
	return ioutil.WriteFile(segmentPath, js, 0644)
}

// GetSegment implements github.com/stratumn/go/store.Adapter.GetSegment.
func (a *FileStore) GetSegment(linkHash *types.Bytes32) (*cs.Segment, error) {
	file, err := os.Open(path.Join(a.config.Path, linkHash.String()+".json"))
	if os.IsNotExist(err) {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}

	defer file.Close()

	var segment cs.Segment
	if err = json.NewDecoder(file).Decode(&segment); err != nil {
		return nil, err
	}

	return &segment, nil
}

// DeleteSegment implements github.com/stratumn/go/store.Adapter.DeleteSegment.
func (a *FileStore) DeleteSegment(linkHash *types.Bytes32) (*cs.Segment, error) {
	segment, err := a.GetSegment(linkHash)
	if segment == nil {
		return segment, err
	}

	if err = os.Remove(path.Join(a.config.Path, linkHash.String()+".json")); err != nil {
		return nil, err
	}

	return segment, err
}

// FindSegments implements github.com/stratumn/go/store.Adapter.FindSegments.
func (a *FileStore) FindSegments(filter *store.Filter) (cs.SegmentSlice, error) {
	var segments cs.SegmentSlice

	a.forEach(func(segment *cs.Segment) error {
		if filter.MapID != "" && filter.MapID != segment.Link.Meta["mapId"].(string) {
			return nil
		}

		if filter.PrevLinkHash != nil {
			prevLinkHash, ok := segment.Link.Meta["prevLinkHash"].(string)
			if !ok || filter.PrevLinkHash.String() != prevLinkHash {
				return nil
			}
		}

		if len(filter.Tags) > 0 {
			if t, ok := segment.Link.Meta["tags"].([]interface{}); ok {
				var tags []string
				for _, v := range t {
					tags = append(tags, v.(string))
				}

				for _, tag := range filter.Tags {
					if !containsString(tags, tag) {
						return nil
					}
				}
			} else {
				return nil
			}
		}

		segments = append(segments, segment)

		return nil
	})

	sort.Sort(segments)

	return paginateSegments(segments, &filter.Pagination), nil
}

// GetMapIDs implements github.com/stratumn/go/store.Adapter.GetMapIDs.
func (a *FileStore) GetMapIDs(pagination *store.Pagination) ([]string, error) {
	set := map[string]struct{}{}
	a.forEach(func(segment *cs.Segment) error {
		set[segment.Link.Meta["mapId"].(string)] = struct{}{}
		return nil
	})

	var mapIDs []string
	for mapID := range set {
		mapIDs = append(mapIDs, mapID)
	}

	sort.Strings(mapIDs)
	return paginateStrings(mapIDs, pagination), nil
}

var segmentFileRegepx = regexp.MustCompile("(.*)\\.json$")

func (a *FileStore) forEach(fn func(*cs.Segment) error) error {
	files, err := ioutil.ReadDir(a.config.Path)
	if os.IsNotExist(err) {
		return nil
	}
	if err != nil {
		return err
	}

	for _, file := range files {
		name := file.Name()
		if segmentFileRegepx.MatchString(name) {
			linkHashStr := name[:len(name)-5]
			linkHash, err := types.NewBytes32FromString(linkHashStr)
			if err != nil {
				return err
			}

			segment, err := a.GetSegment(linkHash)
			if err != nil {
				return err
			}
			if segment == nil {
				return fmt.Errorf("could not find segment %q", filepath.Base(name))
			}
			if err = fn(segment); err != nil {
				return err
			}
		}
	}

	return nil
}

func containsString(a []string, s string) bool {
	for _, v := range a {
		if v == s {
			return true
		}
	}

	return false
}

func paginateStrings(a []string, p *store.Pagination) []string {
	l := len(a)

	if p.Offset >= l {
		return []string{}
	}

	if p.Limit > 0 {
		end := min(l, p.Offset+p.Limit)
		return a[p.Offset:end]
	}

	return a[p.Offset:]
}

func paginateSegments(a cs.SegmentSlice, p *store.Pagination) cs.SegmentSlice {
	l := len(a)

	if p.Offset >= l {
		return cs.SegmentSlice{}
	}

	if p.Limit > 0 {
		end := min(l, p.Offset+p.Limit)
		return a[p.Offset:end]
	}

	return a[p.Offset:]
}

// Min of two ints, duh.
func min(a, b int) int {
	if a < b {
		return a
	}

	return b
}
<commit_msg>Ignore IsExist error<commit_after>// Copyright 2016 Stratumn SAS. All rights reserved.
//
// 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 filestore implements a store that saves all the segments to the file system.
//
// The segments are stored as JSON files named after the link hashes.
// It's a convenient store to use during the development of an agent.
// However, because it doesn't use an index, it's very slow, and shouldn't be used for production.
package filestore

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"sort"

	"github.com/stratumn/go/cs"
	"github.com/stratumn/go/store"
	"github.com/stratumn/go/types"
)

const (
	// Name is the name set in the store's information.
	Name = "file"

	// Description is the description set in the store's information.
	Description = "Stratumn File Store"

	// DefaultPath is the path where segments will be saved by default.
	DefaultPath = "/var/filestore"
)

// FileStore is the type that implements github.com/stratumn/go/store.Adapter.
type FileStore struct {
	config *Config
}

// Config contains configuration options for the store.
type Config struct {
	// A version string that will be set in the store's information.
	Version string

	// A git commit hash that will be set in the store's information.
	Commit string

	// Path where segments will be saved.
	Path string
}

// Info is the info returned by GetInfo.
type Info struct {
	Name        string `json:"name"`
	Description string `json:"description"`
	Version     string `json:"version"`
	Commit      string `json:"commit"`
}

// New creates an instance of a FileStore.
func New(config *Config) *FileStore {
	return &FileStore{config}
}

// GetInfo implements github.com/stratumn/go/store.Adapter.GetInfo.
func (a *FileStore) GetInfo() (interface{}, error) {
	return &Info{
		Name:        Name,
		Description: Description,
		Version:     a.config.Version,
		Commit:      a.config.Commit,
	}, nil
}

// SaveSegment implements github.com/stratumn/go/store.Adapter.SaveSegment.
func (a *FileStore) SaveSegment(segment *cs.Segment) error {
	js, err := json.MarshalIndent(segment, "", "  ")
	if err != nil {
		return err
	}

	if err = os.MkdirAll(a.config.Path, 0755); err != nil {
		if !os.IsExist(err) {
			return err
		}
	}

	segmentPath := path.Join(a.config.Path, segment.Meta["linkHash"].(string)+".json")
	return ioutil.WriteFile(segmentPath, js, 0644)
}

// GetSegment implements github.com/stratumn/go/store.Adapter.GetSegment.
func (a *FileStore) GetSegment(linkHash *types.Bytes32) (*cs.Segment, error) {
	file, err := os.Open(path.Join(a.config.Path, linkHash.String()+".json"))
	if os.IsNotExist(err) {
		return nil, nil
	}
	if err != nil {
		return nil, err
	}

	defer file.Close()

	var segment cs.Segment
	if err = json.NewDecoder(file).Decode(&segment); err != nil {
		return nil, err
	}

	return &segment, nil
}

// DeleteSegment implements github.com/stratumn/go/store.Adapter.DeleteSegment.
func (a *FileStore) DeleteSegment(linkHash *types.Bytes32) (*cs.Segment, error) {
	segment, err := a.GetSegment(linkHash)
	if segment == nil {
		return segment, err
	}

	if err = os.Remove(path.Join(a.config.Path, linkHash.String()+".json")); err != nil {
		return nil, err
	}

	return segment, err
}

// FindSegments implements github.com/stratumn/go/store.Adapter.FindSegments.
func (a *FileStore) FindSegments(filter *store.Filter) (cs.SegmentSlice, error) {
	var segments cs.SegmentSlice

	a.forEach(func(segment *cs.Segment) error {
		if filter.MapID != "" && filter.MapID != segment.Link.Meta["mapId"].(string) {
			return nil
		}

		if filter.PrevLinkHash != nil {
			prevLinkHash, ok := segment.Link.Meta["prevLinkHash"].(string)
			if !ok || filter.PrevLinkHash.String() != prevLinkHash {
				return nil
			}
		}

		if len(filter.Tags) > 0 {
			if t, ok := segment.Link.Meta["tags"].([]interface{}); ok {
				var tags []string
				for _, v := range t {
					tags = append(tags, v.(string))
				}

				for _, tag := range filter.Tags {
					if !containsString(tags, tag) {
						return nil
					}
				}
			} else {
				return nil
			}
		}

		segments = append(segments, segment)

		return nil
	})

	sort.Sort(segments)

	return paginateSegments(segments, &filter.Pagination), nil
}

// GetMapIDs implements github.com/stratumn/go/store.Adapter.GetMapIDs.
func (a *FileStore) GetMapIDs(pagination *store.Pagination) ([]string, error) {
	set := map[string]struct{}{}
	a.forEach(func(segment *cs.Segment) error {
		set[segment.Link.Meta["mapId"].(string)] = struct{}{}
		return nil
	})

	var mapIDs []string
	for mapID := range set {
		mapIDs = append(mapIDs, mapID)
	}

	sort.Strings(mapIDs)
	return paginateStrings(mapIDs, pagination), nil
}

var segmentFileRegepx = regexp.MustCompile("(.*)\\.json$")

func (a *FileStore) forEach(fn func(*cs.Segment) error) error {
	files, err := ioutil.ReadDir(a.config.Path)
	if os.IsNotExist(err) {
		return nil
	}
	if err != nil {
		return err
	}

	for _, file := range files {
		name := file.Name()
		if segmentFileRegepx.MatchString(name) {
			linkHashStr := name[:len(name)-5]
			linkHash, err := types.NewBytes32FromString(linkHashStr)
			if err != nil {
				return err
			}

			segment, err := a.GetSegment(linkHash)
			if err != nil {
				return err
			}
			if segment == nil {
				return fmt.Errorf("could not find segment %q", filepath.Base(name))
			}
			if err = fn(segment); err != nil {
				return err
			}
		}
	}

	return nil
}

func containsString(a []string, s string) bool {
	for _, v := range a {
		if v == s {
			return true
		}
	}

	return false
}

func paginateStrings(a []string, p *store.Pagination) []string {
	l := len(a)

	if p.Offset >= l {
		return []string{}
	}

	if p.Limit > 0 {
		end := min(l, p.Offset+p.Limit)
		return a[p.Offset:end]
	}

	return a[p.Offset:]
}

func paginateSegments(a cs.SegmentSlice, p *store.Pagination) cs.SegmentSlice {
	l := len(a)

	if p.Offset >= l {
		return cs.SegmentSlice{}
	}

	if p.Limit > 0 {
		end := min(l, p.Offset+p.Limit)
		return a[p.Offset:end]
	}

	return a[p.Offset:]
}

// Min of two ints, duh.
func min(a, b int) int {
	if a < b {
		return a
	}

	return b
}
<|endoftext|>


<commit_before>package forwarder

import (
	"time"

	log "github.com/cihub/seelog"
)

func Start(traceChan chan []byte, numWorkers, bufferSize int) {

	log.Infof("Starting %v forwarders with buffer size of %v", numWorkers, bufferSize)

	for i := 0; i < numWorkers; i++ {
		f := &forwarder{
			id:            i,
			ch:            traceChan,
			messageBuffer: make([][]byte, 0, bufferSize),
			bufferSize:    bufferSize,
		}

		// Do something useful
		go f.work()
	}

}

type forwarder struct {
	id            int
	ch            chan []byte
	messageBuffer [][]byte
	bufferSize    int
}

func (f *forwarder) work() {

	log.Debugf("[Forwarder %v] started", f.id)

	var b []byte
	var i int

	metricsTick := time.NewTicker(5 * time.Second)
	timeoutTick := time.NewTicker(2 * time.Second)

	for {
		// log.Debugf("[Forwarder %v] Waiting for message", f.id)
		select {
		case b = <-f.ch:
			i++
			// log.Debugf("[Forwarder %v] Received message", f.id)

			// Add message to our buffer
			f.messageBuffer = append(f.messageBuffer, b)

			// Forward on if we're at our buffer size
			if len(f.messageBuffer) >= f.bufferSize {
				f.send()
			}
		case <-timeoutTick.C:
			f.send()
		case <-metricsTick.C:
			log.Debugf("[Forwarder %v] Processed %v messages", f.id, i)
			// i = 0
		}
	}
}

func (f *forwarder) send() {

	log.Infof("[Forwarder %v] Sent %v messages", f.id, len(f.messageBuffer))

	// Empty the buffer
	f.messageBuffer = nil
}
<commit_msg>Log traces to the console while in debug mode<commit_after>package forwarder

import (
	"encoding/json"
	"time"

	log "github.com/cihub/seelog"
	"github.com/golang/protobuf/proto"

	pb "github.com/bankpossible/iamdev/shared/messages"
)

var (
	debug = true
)

func Start(traceChan chan []byte, numWorkers, bufferSize int) {

	log.Infof("Starting %v forwarders with buffer size of %v", numWorkers, bufferSize)

	for i := 0; i < numWorkers; i++ {
		f := &forwarder{
			id:            i,
			ch:            traceChan,
			messageBuffer: make([][]byte, 0, bufferSize),
			bufferSize:    bufferSize,
		}

		// Do something useful
		go f.work()
	}

}

type forwarder struct {
	id            int
	ch            chan []byte
	messageBuffer [][]byte
	bufferSize    int
}

func (f *forwarder) work() {

	log.Debugf("[Forwarder %v] started", f.id)

	var b []byte
	var i int
	var decoded *pb.TraceFrame
	var js []byte

	metricsTick := time.NewTicker(5 * time.Second)
	timeoutTick := time.NewTicker(2 * time.Second)

	for {
		// log.Debugf("[Forwarder %v] Waiting for message", f.id)
		select {
		case b = <-f.ch:
			i++

			// Log the frame if we're in debug mode
			if debug {
				decoded = &pb.TraceFrame{}
				if err := proto.Unmarshal(b, decoded); err != nil {
					log.Infof("[Forwarder %v] Couldn't decode trace frame", f.id)
					continue
				}
				js, _ = json.Marshal(decoded)
				log.Infof("[Forwarder %v] Received message: %s", f.id, string(js))
			}

			// Add message to our buffer
			f.messageBuffer = append(f.messageBuffer, b)

			// Forward on if we're at our buffer size
			if len(f.messageBuffer) >= f.bufferSize {
				f.send()
			}
		case <-timeoutTick.C:
			f.send()
		case <-metricsTick.C:
			log.Debugf("[Forwarder %v] Processed %v messages", f.id, i)
		}
	}
}

func (f *forwarder) send() {

	log.Infof("[Forwarder %v] Sent %v messages", f.id, len(f.messageBuffer))

	// Empty the buffer
	f.messageBuffer = nil
}
<|endoftext|>


<commit_before>package ec2

import (
	. "launchpad.net/gocheck"
	"launchpad.net/goyaml"
	"regexp"
)

// Use local suite since this file lives in the ec2 package
// for testing internals.
type cloudinitSuite struct{}

var _ = Suite(cloudinitSuite{})

// Each test gives a cloudinit config - we check the
// output to see if it looks correct.
var cloudinitTests = []cloudConfig{
	{
		adminSecret:        "topsecret",
		instanceIdAccessor: "$instance_id",
		machineId:          "aMachine",
		origin:             jujuOrigin{originBranch, "lp:jujubranch"},
		providerType:       "ec2",
		provisioner:        true,
		sshKeys:            []string{"sshkey1"},
		zookeeper:          true,
	},
	{
		adminSecret:    "topsecret",
		machineId:      "aMachine",
		origin:         jujuOrigin{originDistro, ""},
		providerType:   "ec2",
		provisioner:    false,
		sshKeys:        []string{"sshkey1"},
		zookeeper:      false,
		zookeeperHosts: []string{"zk1"},
	},
}

// cloundInitTest runs a set of tests for one of the cloudConfig
// values above.
type cloudinitTest struct {
	x   map[interface{}]interface{} // the unmarshalled YAML.
	cfg *cloudConfig                // the config being tested.
}

func (t *cloudinitTest) check(c *C) {
	t.checkPackage(c, "bzr")
	t.checkOption(c, "apt_upgrade", true)
	t.checkOption(c, "apt_update", true)
	t.checkScripts(c, "mkdir -p /var/lib/juju")
	t.checkMachineData(c)

	if t.cfg.zookeeper {
		t.checkPackage(c, "zookeeperd")
		t.checkScripts(c, "juju-admin initialize")
		t.checkScripts(c, regexp.QuoteMeta(t.cfg.instanceIdAccessor))
	}
	if t.cfg.origin != (jujuOrigin{}) && t.cfg.origin.origin == originDistro {
		t.checkScripts(c, "apt-get.*install juju")
	}
	if t.cfg.provisioner {
		t.checkScripts(c, "python -m juju.agents.provision")
	}
}

func (t *cloudinitTest) checkMachineData(c *C) {
	mdata0 := t.x["machine-data"]
	c.Assert(mdata0, NotNil)
	mdata, ok := mdata0.(map[interface{}]interface{})
	c.Assert(ok, Equals, true)

	m := mdata["machine-id"]
	c.Assert(m, Equals, t.cfg.machineId)
}

// checkOption checks that the given option name has the expected value.
func (t *cloudinitTest) checkOption(c *C, name string, value interface{}) {
	c.Check(t.x[name], Equals, value, Bug("option %q", name))
}

// checkScripts checks that at least one script started by
// the cloudinit matches the given regexp pattern.
func (t *cloudinitTest) checkScripts(c *C, pattern string) {
	scripts0 := t.x["runcmd"]
	if !c.Check(scripts0, NotNil, Bug("cloudinit has no entry for runcmd")) {
		return
	}
	scripts, ok := scripts0.([]interface{})
	if !c.Check(ok, Equals, true, Bug("runcmd entry is wrong type; got %T want []interface{}", scripts0)) {
		return
	}
	re := regexp.MustCompile(pattern)
	found := false
	for _, s0 := range scripts {
		s, ok := s0.(string)
		if !c.Check(ok, Equals, true, Bug("script entry has unexpected type %T want string", s0)) {
			continue
		}
		if re.MatchString(s) {
			found = true
		}
	}
	c.Check(found, Equals, true, Bug("script %q not found", pattern))
}

// checkPackage checks that the cloudinit will install the given package.
func (t *cloudinitTest) checkPackage(c *C, pkg string) {
	pkgs0 := t.x["packages"]
	if !c.Check(pkgs0, NotNil, Bug("cloudinit has no entry for packages")) {
		return
	}

	pkgs, ok := pkgs0.([]interface{})
	if !c.Check(ok, Equals, true, Bug("cloudinit packages entry is wrong type; got %T want []interface{}", pkgs0)) {
		return
	}

	found := false
	for _, p0 := range pkgs {
		p, ok := p0.(string)
		c.Check(ok, Equals, true, Bug("cloudinit package entry is wrong type; got %T want string", p0))
		if p == pkg {
			found = true
		}
	}
	c.Check(found, Equals, true, Bug("%q not found in packages", pkg))
}

// TestCloudInit checks that the output from the various tests
// in cloudinitTests is well formed.
func (cloudinitSuite) TestCloudInit(c *C) {
	for i, cfg := range cloudinitTests {
		c.Logf("check %d", i)
		ci, err := newCloudInit(&cfg)
		c.Assert(err, IsNil)
		c.Check(ci, NotNil)

		// render the cloudinit config to bytes, and then
		// back to a map so we can introspect it without
		// worrying about internal details of the cloudinit
		// package.

		data, err := ci.Render()
		c.Assert(err, IsNil)

		x := make(map[interface{}]interface{})
		err = goyaml.Unmarshal(data, &x)
		c.Assert(err, IsNil)

		c.Logf("result %v", x)
		t := &cloudinitTest{
			cfg: &cfg,
			x:   x,
		}
		t.check(c)
	}
}

// When mutate is called on a known-good cloudConfig,
// there should be an error complaining about the missing
// field named by the adjacent err.
var verifyTests = []struct {
	err    string
	mutate func(*cloudConfig)
}{
	{"machine id", func(cfg *cloudConfig) { cfg.machineId = "" }},
	{"provider type", func(cfg *cloudConfig) { cfg.providerType = "" }},
	{"instance id accessor", func(cfg *cloudConfig) {
		cfg.zookeeper = true
		cfg.instanceIdAccessor = ""
	}},
	{"admin secret", func(cfg *cloudConfig) {
		cfg.zookeeper = true
		cfg.adminSecret = ""
	}},
	{"zookeeper hosts", func(cfg *cloudConfig) {
		cfg.zookeeper = false
		cfg.zookeeperHosts = nil
	}},
}

// TestCloudInitVerify checks that required fields are appropriately
// checked for by newCloudInit.
func (cloudinitSuite) TestCloudInitVerify(c *C) {
	cfg := &cloudConfig{
		provisioner:        true,
		zookeeper:          true,
		instanceIdAccessor: "$instance_id",
		adminSecret:        "topsecret",
		providerType:       "ec2",
		origin:             jujuOrigin{originBranch, "lp:jujubranch"},
		machineId:          "aMachine",
		sshKeys:            []string{"sshkey1"},
		zookeeperHosts:     []string{"zkhost"},
	}
	// check that the base configuration does not give an error
	_, err := newCloudInit(cfg)
	c.Assert(err, IsNil)

	for _, test := range verifyTests {
		cfg1 := *cfg
		test.mutate(&cfg1)
		t, err := newCloudInit(&cfg1)
		c.Assert(err, ErrorMatches, "cloud configuration requires "+test.err)
		c.Assert(t, IsNil)
	}
}

var policyTests = []struct {
	policy string
	origin jujuOrigin
}{
	{`
		|juju:
		|  Installed: 0.5+bzr411-1juju1~natty1
		|  Candidate: 0.5+bzr411-1juju1~natty1
		|  Version table:
		| *** 0.5+bzr411-1juju1~natty1 0
		|        100 /var/lib/dpkg/status
		|     0.5+bzr398-0ubuntu1 0
		|        500 http://gb.archive.ubuntu.com/ubuntu/ oneiric/universe amd64 Packages`,
		jujuOrigin{
			originDistro,
			"",
		},
		// TODO add more tests with real output from apt_cache
	},
}

var unindentPattern = regexp.MustCompile(`\n\s*\|`)

// If the string doesn't start with a newline, unindent returns
// it. Otherwise it removes the initial newline and the
// indentation from each line of the string and adds a trailing newline.
// Indentation is defined to be
// a run of white space followed by a '|' character.
func unindent(s string) string {
	if s[0] != '\n' {
		return s
	}
	return unindentPattern.ReplaceAllString(s, "\n")[1:] + "\n"
}

func (cloudinitSuite) TestCloudPolicyToOrigin(c *C) {
	for i, t := range policyTests {
		o := policyToOrigin(unindent(t.policy) + "\n")
		c.Check(o, Equals, t.origin, Bug("test %d", i))
	}
}
<commit_msg>added more origin tests<commit_after>package ec2

import (
	. "launchpad.net/gocheck"
	"launchpad.net/goyaml"
	"regexp"
)

// Use local suite since this file lives in the ec2 package
// for testing internals.
type cloudinitSuite struct{}

var _ = Suite(cloudinitSuite{})

// Each test gives a cloudinit config - we check the
// output to see if it looks correct.
var cloudinitTests = []cloudConfig{
	{
		adminSecret:        "topsecret",
		instanceIdAccessor: "$instance_id",
		machineId:          "aMachine",
		origin:             jujuOrigin{originBranch, "lp:jujubranch"},
		providerType:       "ec2",
		provisioner:        true,
		sshKeys:            []string{"sshkey1"},
		zookeeper:          true,
	},
	{
		adminSecret:    "topsecret",
		machineId:      "aMachine",
		origin:         jujuOrigin{originDistro, ""},
		providerType:   "ec2",
		provisioner:    false,
		sshKeys:        []string{"sshkey1"},
		zookeeper:      false,
		zookeeperHosts: []string{"zk1"},
	},
}

// cloundInitTest runs a set of tests for one of the cloudConfig
// values above.
type cloudinitTest struct {
	x   map[interface{}]interface{} // the unmarshalled YAML.
	cfg *cloudConfig                // the config being tested.
}

func (t *cloudinitTest) check(c *C) {
	t.checkPackage(c, "bzr")
	t.checkOption(c, "apt_upgrade", true)
	t.checkOption(c, "apt_update", true)
	t.checkScripts(c, "mkdir -p /var/lib/juju")
	t.checkMachineData(c)

	if t.cfg.zookeeper {
		t.checkPackage(c, "zookeeperd")
		t.checkScripts(c, "juju-admin initialize")
		t.checkScripts(c, regexp.QuoteMeta(t.cfg.instanceIdAccessor))
	}
	if t.cfg.origin != (jujuOrigin{}) && t.cfg.origin.origin == originDistro {
		t.checkScripts(c, "apt-get.*install juju")
	}
	if t.cfg.provisioner {
		t.checkScripts(c, "python -m juju.agents.provision")
	}
}

func (t *cloudinitTest) checkMachineData(c *C) {
	mdata0 := t.x["machine-data"]
	c.Assert(mdata0, NotNil)
	mdata, ok := mdata0.(map[interface{}]interface{})
	c.Assert(ok, Equals, true)

	m := mdata["machine-id"]
	c.Assert(m, Equals, t.cfg.machineId)
}

// checkOption checks that the given option name has the expected value.
func (t *cloudinitTest) checkOption(c *C, name string, value interface{}) {
	c.Check(t.x[name], Equals, value, Bug("option %q", name))
}

// checkScripts checks that at least one script started by
// the cloudinit matches the given regexp pattern.
func (t *cloudinitTest) checkScripts(c *C, pattern string) {
	scripts0 := t.x["runcmd"]
	if !c.Check(scripts0, NotNil, Bug("cloudinit has no entry for runcmd")) {
		return
	}
	scripts, ok := scripts0.([]interface{})
	if !c.Check(ok, Equals, true, Bug("runcmd entry is wrong type; got %T want []interface{}", scripts0)) {
		return
	}
	re := regexp.MustCompile(pattern)
	found := false
	for _, s0 := range scripts {
		s, ok := s0.(string)
		if !c.Check(ok, Equals, true, Bug("script entry has unexpected type %T want string", s0)) {
			continue
		}
		if re.MatchString(s) {
			found = true
		}
	}
	c.Check(found, Equals, true, Bug("script %q not found", pattern))
}

// checkPackage checks that the cloudinit will install the given package.
func (t *cloudinitTest) checkPackage(c *C, pkg string) {
	pkgs0 := t.x["packages"]
	if !c.Check(pkgs0, NotNil, Bug("cloudinit has no entry for packages")) {
		return
	}

	pkgs, ok := pkgs0.([]interface{})
	if !c.Check(ok, Equals, true, Bug("cloudinit packages entry is wrong type; got %T want []interface{}", pkgs0)) {
		return
	}

	found := false
	for _, p0 := range pkgs {
		p, ok := p0.(string)
		c.Check(ok, Equals, true, Bug("cloudinit package entry is wrong type; got %T want string", p0))
		if p == pkg {
			found = true
		}
	}
	c.Check(found, Equals, true, Bug("%q not found in packages", pkg))
}

// TestCloudInit checks that the output from the various tests
// in cloudinitTests is well formed.
func (cloudinitSuite) TestCloudInit(c *C) {
	for i, cfg := range cloudinitTests {
		c.Logf("check %d", i)
		ci, err := newCloudInit(&cfg)
		c.Assert(err, IsNil)
		c.Check(ci, NotNil)

		// render the cloudinit config to bytes, and then
		// back to a map so we can introspect it without
		// worrying about internal details of the cloudinit
		// package.

		data, err := ci.Render()
		c.Assert(err, IsNil)

		x := make(map[interface{}]interface{})
		err = goyaml.Unmarshal(data, &x)
		c.Assert(err, IsNil)

		c.Logf("result %v", x)
		t := &cloudinitTest{
			cfg: &cfg,
			x:   x,
		}
		t.check(c)
	}
}

// When mutate is called on a known-good cloudConfig,
// there should be an error complaining about the missing
// field named by the adjacent err.
var verifyTests = []struct {
	err    string
	mutate func(*cloudConfig)
}{
	{"machine id", func(cfg *cloudConfig) { cfg.machineId = "" }},
	{"provider type", func(cfg *cloudConfig) { cfg.providerType = "" }},
	{"instance id accessor", func(cfg *cloudConfig) {
		cfg.zookeeper = true
		cfg.instanceIdAccessor = ""
	}},
	{"admin secret", func(cfg *cloudConfig) {
		cfg.zookeeper = true
		cfg.adminSecret = ""
	}},
	{"zookeeper hosts", func(cfg *cloudConfig) {
		cfg.zookeeper = false
		cfg.zookeeperHosts = nil
	}},
}

// TestCloudInitVerify checks that required fields are appropriately
// checked for by newCloudInit.
func (cloudinitSuite) TestCloudInitVerify(c *C) {
	cfg := &cloudConfig{
		provisioner:        true,
		zookeeper:          true,
		instanceIdAccessor: "$instance_id",
		adminSecret:        "topsecret",
		providerType:       "ec2",
		origin:             jujuOrigin{originBranch, "lp:jujubranch"},
		machineId:          "aMachine",
		sshKeys:            []string{"sshkey1"},
		zookeeperHosts:     []string{"zkhost"},
	}
	// check that the base configuration does not give an error
	_, err := newCloudInit(cfg)
	c.Assert(err, IsNil)

	for _, test := range verifyTests {
		cfg1 := *cfg
		test.mutate(&cfg1)
		t, err := newCloudInit(&cfg1)
		c.Assert(err, ErrorMatches, "cloud configuration requires "+test.err)
		c.Assert(t, IsNil)
	}
}

var policyTests = []struct {
	policy string
	origin jujuOrigin
}{
	{`
		|juju:
		|  Installed: 0.5+bzr411-1juju1~natty1
		|  Candidate: 0.5+bzr411-1juju1~natty1
		|  Version table:
		| *** 0.5+bzr411-1juju1~natty1 0
		|        100 /var/lib/dpkg/status
		|     0.5+bzr398-0ubuntu1 0
		|        500 http://gb.archive.ubuntu.com/ubuntu/ oneiric/universe amd64 Packages`,
		jujuOrigin{
			originDistro,
			"",
		},
	},
	{`
		|juju:
		|  Installed: good-magic-1.0
		|  Candidate: good-magic-1.0
		|  Version table:
		| *** good-magic-1.0
		|        500 http://us.archive.ubuntu.com/ubuntu/ natty/main amd64 Packages
		|        100 /var/lib/dpkg/status`,
		jujuOrigin{originDistro, ""},
	}, {`
		|juju:
		|  Installed: good-magic-1.0
		|  Candidate: good-magic-1.0
		|  Version table:
		|     0.5+bzr366-1juju1~natty1 0
		|        500 http://ppa.launchpad.net/juju/pkgs/ubuntu/ natty/main amd64 Packages
		| *** good-magic-1.0 0
		|        500 http://us.archive.ubuntu.com/ubuntu/ natty/main amd64 Packages
		|        100 /var/lib/dpkg/status`,
		jujuOrigin{originDistro, ""},
	}, {`
		|juju:
		|  Installed: 0.5+bzr366-1juju1~natty1
		|  Candidate: 0.5+bzr366-1juju1~natty1
		|  Version table:
		|     bad-magic-0.5 0
		|        500 http://us.archive.ubuntu.com/ubuntu/ natty/main amd64 Packages
		| *** 0.5+bzr366-1juju1~natty1 0
		|        100 /var/lib/dpkg/status
		|        500 http://ppa.launchpad.net/juju/pkgs/ubuntu/ natty/main amd64 Packages
		|     0.5+bzr356-1juju1~natty1 0
		|        500 http://us.archive.ubuntu.com/ubuntu/ natty/main amd64 Packages`,
		jujuOrigin{originPPA, ""},
	}, {`
		|juju:
		|  Installed: (none)
		|  Candidate: good-magic-1.0
		|  Version table:
		|     0.5+bzr366-1juju1~natty1 0
		|        100 /var/lib/dpkg/status
		|        500 http://ppa.launchpad.net/juju/pkgs/ubuntu/ natty/main amd64 Packages
		|     good-magic-1.0 0
		|        500 http://us.archive.ubuntu.com/ubuntu/ natty/main amd64 Packages
		|        100 /var/lib/dpkg/status`,
		jujuOrigin{originBranch, "lp:juju"},
	}, {`
		|juju:
		|  Installed: 0.5+bzr356-1juju1~natty1
		|  Candidate: 0.5+bzr356-1juju1~natty1
		|  Version table:
		|     good-magic-1.0 0
		|        500 http://us.archive.ubuntu.com/ubuntu/ natty/main amd64 Packages
		| *** 0.5+bzr356-1juju1~natty1 0
		|        500 http://ppa.launchpad.net/juju/pkgs/ubuntu/ natty/main amd64 Packages
		|        100 /var/lib/dpkg/status`,
		jujuOrigin{originPPA, ""},
	}, {`
		|juju:
		|  Installed: whatever
		|  Candidate: whatever-else
		|  Nothing interesting here:`,
		jujuOrigin{originDistro, ""},
	}, {`N: VAT GEEV?`,
		jujuOrigin{originDistro, ""},
	}, {`
		|juju:
		|  Installed: good-magic-1.0
		|  Candidate: good-magic-1.0
		|  Version table:
		| *** good-magic-1.0 0
		|        500 http://ppa.launchpad.net/juju/pkgs/ubuntu/ natty/main amd64 Packages
		|        100 /var/lib/dpkg/status`,
		jujuOrigin{originPPA, ""},
	},
}

var unindentPattern = regexp.MustCompile(`\n\s*\|`)

// If the string doesn't start with a newline, unindent returns
// it. Otherwise it removes the initial newline and the
// indentation from each line of the string and adds a trailing newline.
// Indentation is defined to be
// a run of white space followed by a '|' character.
func unindent(s string) string {
	if s[0] != '\n' {
		return s
	}
	return unindentPattern.ReplaceAllString(s, "\n")[1:] + "\n"
}

func (cloudinitSuite) TestCloudPolicyToOrigin(c *C) {
	for i, t := range policyTests {
		o := policyToOrigin(unindent(t.policy) + "\n")
		c.Check(o, Equals, t.origin, Bug("test %d", i))
	}
}
<|endoftext|>


<commit_before>package postgres

import (
	"database/sql/driver"
	"errors"
	"fmt"
	"regexp"
	"strings"

	"github.com/lib/pq"

	"github.com/archsh/go.xql"
)

type Elemented interface {
	Elem2Strings() []string
	Strings2Elem(...string) error
}

type StringArray []string

func (h StringArray) Declare(props xql.PropertySet) string {
	size, _ := props.GetInt("size", 32)
	return fmt.Sprintf("varchar(%d)[]", size)
}
//
//func (a StringArray) Elem2Strings() []string {
//	var ss []string
//	for _, x := range a {
//		ss = append(ss,
//			strings.Replace(
//				strings.Replace(x, `'`, `\'`, -1), `"`, `\"`, -1))
//	}
//	return ss
//}
//
//func (a *StringArray) Strings2Elem(ss ...string) error {
//	(*a) = ss
//	return nil
//}

func (p *StringArray) Scan(src interface{}) error {
	return pq.Array(p).Scan(src)
	//return Array_Scan(src, p)
}

func (p StringArray) Value() (driver.Value, error) {
	return pq.Array(p).Value()
	//return Array_Value(&p)
}

type IntegerArray []int

func (h IntegerArray) Declare(props xql.PropertySet) string {
	return "integer[]"
}

//func (a IntegerArray) Elem2Strings() []string {
//	ss := []string{}
//	for _, x := range a {
//		ss = append(ss, fmt.Sprintf("%d", x))
//	}
//	return ss
//}
//
//func (a *IntegerArray) Strings2Elem(ss ...string) error {
//	for _, s := range ss {
//		n, e := strconv.ParseInt(s, 10, 32)
//		if nil != e {
//			return e
//		} else {
//			(*a) = append(*a, int(n))
//		}
//	}
//	return nil
//}

func (p *IntegerArray) Scan(src interface{}) error {
	return pq.Array(p).Scan(src)
	//return Array_Scan(src, p)
}

func (p IntegerArray) Value() (driver.Value, error) {
	return pq.Array(p).Value()
	//return Array_Value(&p)
}

type SmallIntegerArray []int16

func (h SmallIntegerArray) Declare(props xql.PropertySet) string {
	return "smallint[]"
}

//func (a SmallIntegerArray) Elem2Strings() []string {
//	ss := []string{}
//	for _, x := range a {
//		ss = append(ss, fmt.Sprintf("%d", x))
//	}
//	return ss
//}
//
//func (a *SmallIntegerArray) Strings2Elem(ss ...string) error {
//	for _, s := range ss {
//		n, e := strconv.ParseInt(s, 10, 16)
//		if nil != e {
//			return e
//		} else {
//			(*a) = append(*a, int16(n))
//		}
//	}
//	return nil
//}

func (p *SmallIntegerArray) Scan(src interface{}) error {
	return pq.Array(p).Scan(src)
	//return Array_Scan(src, p)
}

func (p SmallIntegerArray) Value() (driver.Value, error) {
	return pq.Array(p).Value()
	//return Array_Value(&p)
}

type BigIntegerArray []int64

func (h BigIntegerArray) Declare(props xql.PropertySet) string {
	return "bigint[]"
}

//func (a BigIntegerArray) Elem2Strings() []string {
//	ss := []string{}
//	for _, x := range a {
//		ss = append(ss, fmt.Sprintf("%d", x))
//	}
//	return ss
//}
//
//func (a *BigIntegerArray) Strings2Elem(ss ...string) error {
//	for _, s := range ss {
//		n, e := strconv.ParseInt(s, 10, 64)
//		if nil != e {
//			return e
//		} else {
//			(*a) = append(*a, n)
//		}
//	}
//	return nil
//}

func (p *BigIntegerArray) Scan(src interface{}) error {
	return pq.Array(p).Scan(src)
	//return Array_Scan(src, p)
}

func (p BigIntegerArray) Value() (driver.Value, error) {
	return pq.Array(p).Value()
	//return Array_Value(&p)
}

type RealArray []float32

func (h RealArray) Declare(props xql.PropertySet) string {
	return "real[]"
}

//func (a RealArray) Elem2Strings() []string {
//	ss := []string{}
//	for _, x := range a {
//		ss = append(ss, fmt.Sprintf("%f", x))
//	}
//	return ss
//}
//
//func (a *RealArray) Strings2Elem(ss ...string) error {
//	for _, s := range ss {
//		n, e := strconv.ParseFloat(s, 32)
//		if nil != e {
//			return e
//		} else {
//			(*a) = append(*a, float32(n))
//		}
//	}
//	return nil
//}

func (p *RealArray) Scan(src interface{}) error {
	return pq.Array(p).Scan(src)
	//return Array_Scan(src, p)
}

func (p RealArray) Value() (driver.Value, error) {
	return pq.Array(p).Value()
	//return Array_Value(&p)
}

type DoubleArray []float64

func (h DoubleArray) Declare(props xql.PropertySet) string {
	return "double[]"
}

//func (a DoubleArray) Elem2Strings() []string {
//	ss := []string{}
//	for _, x := range a {
//		ss = append(ss, fmt.Sprintf("%f", x))
//	}
//	return ss
//}
//
//func (a *DoubleArray) Strings2Elem(ss ...string) error {
//	for _, s := range ss {
//		n, e := strconv.ParseFloat(s, 64)
//		if nil != e {
//			return e
//		} else {
//			(*a) = append(*a, n)
//		}
//	}
//	return nil
//}

func (p *DoubleArray) Scan(src interface{}) error {
	return pq.Array(p).Scan(src)
	//return Array_Scan(src, p)
}

func (p DoubleArray) Value() (driver.Value, error) {
	return pq.Array(p).Value()
	//return Array_Value(&p)
}

type BoolArray []bool

func (h BoolArray) Declare(props xql.PropertySet) string {
	return "bool[]"
}

//func (a BoolArray) Elem2Strings() []string {
//	ss := []string{}
//	for _, x := range a {
//		ss = append(ss, fmt.Sprintf("%s", x))
//	}
//	return ss
//}
//
//func (a *BoolArray) Strings2Elem(ss ...string) error {
//	for _, s := range ss {
//		switch strings.ToLower(s) {
//		case "y", "yes", "t", "true", "ok":
//			(*a) = append(*a, true)
//		default:
//			(*a) = append(*a, false)
//		}
//	}
//	return nil
//}

func (p *BoolArray) Scan(src interface{}) error {
	return pq.Array(p).Scan(src)
	//return Array_Scan(src, p)
}

func (p BoolArray) Value() (driver.Value, error) {
	return pq.Array(p).Value()
	//return Array_Value(&p)
}

// PARSING ARRAYS
// SEE http://www.postgresql.org/docs/9.1/static/arrays.html#ARRAYS-IO
// Arrays are output within {} and a delimiter, which is a comma for most
// postgres types (; for box)
//
// Individual values are surrounded by quotes:
// The array output routine will put double quotes around element values if
// they are empty strings, contain curly braces, delimiter characters,
// double quotes, backslashes, or white space, or match the word NULL.
// Double quotes and backslashes embedded in element values will be
// backslash-escaped. For numeric data types it is safe to assume that double
// quotes will never appear, but for textual data types one should be prepared
// to cope with either the presence or absence of quotes.

// construct a regexp to extract values:
var (
	// unquoted array values must not contain: (" , \ { } whitespace NULL)
	// and must be at least one char
	unquotedChar  = `[^",\\{}\s(NULL)]`
	unquotedValue = fmt.Sprintf("(%s)+", unquotedChar)

	// quoted array values are surrounded by double quotes, can be any
	// character except " or \, which must be backslash escaped:
	quotedChar  = `[^"\\]|\\"|\\\\`
	quotedValue = fmt.Sprintf("\"(%s)*\"", quotedChar)

	// an array value may be either quoted or unquoted:
	arrayValue = fmt.Sprintf("(?P<value>(%s|%s))", unquotedValue, quotedValue)

	// Array values are separated with a comma IF there is more than one value:
	arrayExp = regexp.MustCompile(fmt.Sprintf("((%s)(,)?)", arrayValue))

	valueIndex int
)

// Find the index of the 'value' named expression
func init() {
	for i, subexp := range arrayExp.SubexpNames() {
		if subexp == "value" {
			valueIndex = i
			break
		}
	}
}

// Parse the output string from the array type.
// Regex used: (((?P<value>(([^",\\{}\s(NULL)])+|"([^"\\]|\\"|\\\\)*")))(,)?)
func parseArray(array string) []string {
	results := make([]string, 0)
	matches := arrayExp.FindAllStringSubmatch(array, -1)
	for _, match := range matches {
		s := match[valueIndex]
		// the string _might_ be wrapped in quotes, so trim them:
		s = strings.Trim(s, "\"")
		results = append(results, s)
	}
	return results
}

//func (p *StringArray) Scan(src interface{}) error {
//    asBytes, ok := src.([]byte)
//    if !ok {
//        return error(errors.New("Scan source was not []bytes."))
//    }
//
//    asString := string(asBytes)
//    parsed := parseArray(asString)
//    (*p) = StringArray(parsed)
//
//    return nil
//}
//
//func (p StringArray) Value() (driver.Value, error) {
//    var ss []string
//    for _, s := range p {
//        ss = append(ss, fmt.Sprintf(`"%s"`, s))
//    }
//    return strings.Join([]string{"{", strings.Join(ss, ","), "}"}, ""), nil
//}

func Array_Scan(src interface{}, dest interface{}) error {
	if nil == src || dest == nil {
		return nil
	}
	asBytes, ok := src.([]byte)
	if !ok {
		return error(errors.New("Scan source was not []bytes."))
	}

	asString := string(asBytes)
	parsed := parseArray(asString)
	if vv, ok := dest.(Elemented); ok {
		return vv.Strings2Elem(parsed...)
	}
	return errors.New("Elemented should be implemented.")
}

func Array_Value(v interface{}) (driver.Value, error) {
	if nil == v {
		return nil, nil
	}
	if vv, ok := v.(Elemented); ok {
		return strings.Join([]string{"{", strings.Join(vv.Elem2Strings(), ","), "}"}, ""), nil
	}
	return nil, errors.New("Elemented should be implemented.")
}
<commit_msg>Updated: Change to use pq.Array<commit_after>package postgres

import (
	"database/sql/driver"
	"errors"
	"fmt"
	"regexp"
	"strings"

	"github.com/lib/pq"

	"github.com/archsh/go.xql"
)

type Elemented interface {
	Elem2Strings() []string
	Strings2Elem(...string) error
}

type StringArray []string

func (h StringArray) Declare(props xql.PropertySet) string {
	size, _ := props.GetInt("size", 32)
	return fmt.Sprintf("varchar(%d)[]", size)
}

//
//func (a StringArray) Elem2Strings() []string {
//	var ss []string
//	for _, x := range a {
//		ss = append(ss,
//			strings.Replace(
//				strings.Replace(x, `'`, `\'`, -1), `"`, `\"`, -1))
//	}
//	return ss
//}
//
//func (a *StringArray) Strings2Elem(ss ...string) error {
//	(*a) = ss
//	return nil
//}

func (p StringArray) Scan(src interface{}) error {
	var ps []string
	if e := pq.Array(ps).Scan(src); nil != e {
		return e
	} else {
		p = StringArray(ps)
		return nil
	}
	//return pq.Array(p).Scan(src)
	//return Array_Scan(src, p)
}

func (p StringArray) Value() (driver.Value, error) {
	return pq.Array([]string(p)).Value()
	//return Array_Value(&p)
}

type IntegerArray []int

func (h IntegerArray) Declare(props xql.PropertySet) string {
	return "integer[]"
}

//func (a IntegerArray) Elem2Strings() []string {
//	ss := []string{}
//	for _, x := range a {
//		ss = append(ss, fmt.Sprintf("%d", x))
//	}
//	return ss
//}
//
//func (a *IntegerArray) Strings2Elem(ss ...string) error {
//	for _, s := range ss {
//		n, e := strconv.ParseInt(s, 10, 32)
//		if nil != e {
//			return e
//		} else {
//			(*a) = append(*a, int(n))
//		}
//	}
//	return nil
//}

func (p *IntegerArray) Scan(src interface{}) error {
	return pq.Array(p).Scan(src)
	//return Array_Scan(src, p)
}

func (p IntegerArray) Value() (driver.Value, error) {
	return pq.Array(p).Value()
	//return Array_Value(&p)
}

type SmallIntegerArray []int16

func (h SmallIntegerArray) Declare(props xql.PropertySet) string {
	return "smallint[]"
}

//func (a SmallIntegerArray) Elem2Strings() []string {
//	ss := []string{}
//	for _, x := range a {
//		ss = append(ss, fmt.Sprintf("%d", x))
//	}
//	return ss
//}
//
//func (a *SmallIntegerArray) Strings2Elem(ss ...string) error {
//	for _, s := range ss {
//		n, e := strconv.ParseInt(s, 10, 16)
//		if nil != e {
//			return e
//		} else {
//			(*a) = append(*a, int16(n))
//		}
//	}
//	return nil
//}

func (p *SmallIntegerArray) Scan(src interface{}) error {
	return pq.Array(p).Scan(src)
	//return Array_Scan(src, p)
}

func (p SmallIntegerArray) Value() (driver.Value, error) {
	return pq.Array(p).Value()
	//return Array_Value(&p)
}

type BigIntegerArray []int64

func (h BigIntegerArray) Declare(props xql.PropertySet) string {
	return "bigint[]"
}

//func (a BigIntegerArray) Elem2Strings() []string {
//	ss := []string{}
//	for _, x := range a {
//		ss = append(ss, fmt.Sprintf("%d", x))
//	}
//	return ss
//}
//
//func (a *BigIntegerArray) Strings2Elem(ss ...string) error {
//	for _, s := range ss {
//		n, e := strconv.ParseInt(s, 10, 64)
//		if nil != e {
//			return e
//		} else {
//			(*a) = append(*a, n)
//		}
//	}
//	return nil
//}

func (p *BigIntegerArray) Scan(src interface{}) error {
	return pq.Array(p).Scan(src)
	//return Array_Scan(src, p)
}

func (p BigIntegerArray) Value() (driver.Value, error) {
	return pq.Array(p).Value()
	//return Array_Value(&p)
}

type RealArray []float32

func (h RealArray) Declare(props xql.PropertySet) string {
	return "real[]"
}

//func (a RealArray) Elem2Strings() []string {
//	ss := []string{}
//	for _, x := range a {
//		ss = append(ss, fmt.Sprintf("%f", x))
//	}
//	return ss
//}
//
//func (a *RealArray) Strings2Elem(ss ...string) error {
//	for _, s := range ss {
//		n, e := strconv.ParseFloat(s, 32)
//		if nil != e {
//			return e
//		} else {
//			(*a) = append(*a, float32(n))
//		}
//	}
//	return nil
//}

func (p *RealArray) Scan(src interface{}) error {
	return pq.Array(p).Scan(src)
	//return Array_Scan(src, p)
}

func (p RealArray) Value() (driver.Value, error) {
	return pq.Array(p).Value()
	//return Array_Value(&p)
}

type DoubleArray []float64

func (h DoubleArray) Declare(props xql.PropertySet) string {
	return "double[]"
}

//func (a DoubleArray) Elem2Strings() []string {
//	ss := []string{}
//	for _, x := range a {
//		ss = append(ss, fmt.Sprintf("%f", x))
//	}
//	return ss
//}
//
//func (a *DoubleArray) Strings2Elem(ss ...string) error {
//	for _, s := range ss {
//		n, e := strconv.ParseFloat(s, 64)
//		if nil != e {
//			return e
//		} else {
//			(*a) = append(*a, n)
//		}
//	}
//	return nil
//}

func (p *DoubleArray) Scan(src interface{}) error {
	return pq.Array(p).Scan(src)
	//return Array_Scan(src, p)
}

func (p DoubleArray) Value() (driver.Value, error) {
	return pq.Array(p).Value()
	//return Array_Value(&p)
}

type BoolArray []bool

func (h BoolArray) Declare(props xql.PropertySet) string {
	return "bool[]"
}

//func (a BoolArray) Elem2Strings() []string {
//	ss := []string{}
//	for _, x := range a {
//		ss = append(ss, fmt.Sprintf("%s", x))
//	}
//	return ss
//}
//
//func (a *BoolArray) Strings2Elem(ss ...string) error {
//	for _, s := range ss {
//		switch strings.ToLower(s) {
//		case "y", "yes", "t", "true", "ok":
//			(*a) = append(*a, true)
//		default:
//			(*a) = append(*a, false)
//		}
//	}
//	return nil
//}

func (p *BoolArray) Scan(src interface{}) error {
	return pq.Array(p).Scan(src)
	//return Array_Scan(src, p)
}

func (p BoolArray) Value() (driver.Value, error) {
	return pq.Array(p).Value()
	//return Array_Value(&p)
}

// PARSING ARRAYS
// SEE http://www.postgresql.org/docs/9.1/static/arrays.html#ARRAYS-IO
// Arrays are output within {} and a delimiter, which is a comma for most
// postgres types (; for box)
//
// Individual values are surrounded by quotes:
// The array output routine will put double quotes around element values if
// they are empty strings, contain curly braces, delimiter characters,
// double quotes, backslashes, or white space, or match the word NULL.
// Double quotes and backslashes embedded in element values will be
// backslash-escaped. For numeric data types it is safe to assume that double
// quotes will never appear, but for textual data types one should be prepared
// to cope with either the presence or absence of quotes.

// construct a regexp to extract values:
var (
	// unquoted array values must not contain: (" , \ { } whitespace NULL)
	// and must be at least one char
	unquotedChar  = `[^",\\{}\s(NULL)]`
	unquotedValue = fmt.Sprintf("(%s)+", unquotedChar)

	// quoted array values are surrounded by double quotes, can be any
	// character except " or \, which must be backslash escaped:
	quotedChar  = `[^"\\]|\\"|\\\\`
	quotedValue = fmt.Sprintf("\"(%s)*\"", quotedChar)

	// an array value may be either quoted or unquoted:
	arrayValue = fmt.Sprintf("(?P<value>(%s|%s))", unquotedValue, quotedValue)

	// Array values are separated with a comma IF there is more than one value:
	arrayExp = regexp.MustCompile(fmt.Sprintf("((%s)(,)?)", arrayValue))

	valueIndex int
)

// Find the index of the 'value' named expression
func init() {
	for i, subexp := range arrayExp.SubexpNames() {
		if subexp == "value" {
			valueIndex = i
			break
		}
	}
}

// Parse the output string from the array type.
// Regex used: (((?P<value>(([^",\\{}\s(NULL)])+|"([^"\\]|\\"|\\\\)*")))(,)?)
func parseArray(array string) []string {
	results := make([]string, 0)
	matches := arrayExp.FindAllStringSubmatch(array, -1)
	for _, match := range matches {
		s := match[valueIndex]
		// the string _might_ be wrapped in quotes, so trim them:
		s = strings.Trim(s, "\"")
		results = append(results, s)
	}
	return results
}

//func (p *StringArray) Scan(src interface{}) error {
//    asBytes, ok := src.([]byte)
//    if !ok {
//        return error(errors.New("Scan source was not []bytes."))
//    }
//
//    asString := string(asBytes)
//    parsed := parseArray(asString)
//    (*p) = StringArray(parsed)
//
//    return nil
//}
//
//func (p StringArray) Value() (driver.Value, error) {
//    var ss []string
//    for _, s := range p {
//        ss = append(ss, fmt.Sprintf(`"%s"`, s))
//    }
//    return strings.Join([]string{"{", strings.Join(ss, ","), "}"}, ""), nil
//}

func Array_Scan(src interface{}, dest interface{}) error {
	if nil == src || dest == nil {
		return nil
	}
	asBytes, ok := src.([]byte)
	if !ok {
		return error(errors.New("Scan source was not []bytes."))
	}

	asString := string(asBytes)
	parsed := parseArray(asString)
	if vv, ok := dest.(Elemented); ok {
		return vv.Strings2Elem(parsed...)
	}
	return errors.New("Elemented should be implemented.")
}

func Array_Value(v interface{}) (driver.Value, error) {
	if nil == v {
		return nil, nil
	}
	if vv, ok := v.(Elemented); ok {
		return strings.Join([]string{"{", strings.Join(vv.Elem2Strings(), ","), "}"}, ""), nil
	}
	return nil, errors.New("Elemented should be implemented.")
}
<|endoftext|>


<commit_before>/*
Copyright 2014 Huawei Technologies Co., Ltd. All rights reserved.

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 cmd

import (
	"github.com/spf13/cobra"

	"github.com/Huawei/containerops/dockyard/model"
)

// databasecmd is subcommand which migrate/backup/restore Dockyard's database.
var databaseCmd = &cobra.Command{
	Use:   "database",
	Short: "Database subcommand migrate/backup/restore Dockyard's database.",
	Long:  ``,
}

// migrateDatabaseCmd is subcommand migrate Dockyard's database.
var migrateDatabaseCmd = &cobra.Command{
	Use:   "migrate",
	Short: "migrate subcommand migrate Dockyard's database.",
	Long:  ``,
	Run:   migrateDatabase,
}

// backupDatabaseCmd is subcommand backup Dockyard's database.
var backupDatabaseCmd = &cobra.Command{
	Use:   "backup",
	Short: "backup subcommand backup Dockyard's database.",
	Long:  ``,
	Run:   backupDatabase,
}

// restoreDatabaseCmd is subcommand restore Dockyard's database.
var restoreDatabaseCmd = &cobra.Command{
	Use:   "restore",
	Short: "restore subcommand restore Dockyard's database.",
	Long:  ``,
	Run:   restoreDatabase,
}

// init()
func init() {
	RootCmd.AddCommand(databaseCmd)

	databaseCmd.AddCommand(migrateDatabaseCmd)
	databaseCmd.AddCommand(backupDatabaseCmd)
	databaseCmd.AddCommand(restoreDatabaseCmd)
}

// migrateDatabase is auto-migrate database of Dockyard.
func migrateDatabase(cmd *cobra.Command, args []string) {
	models.Migrate()
}

// backupDatabase is
func backupDatabase(cmd *cobra.Command, args []string) {

}

// restoreDatabase is
func restoreDatabase(cmd *cobra.Command, args []string) {

}
<commit_msg>Fix the database command error.<commit_after>/*
Copyright 2014 Huawei Technologies Co., Ltd. All rights reserved.

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 cmd

import (
	"github.com/spf13/cobra"

	"github.com/Huawei/containerops/dockyard/model"
)

// databasecmd is subcommand which migrate/backup/restore Dockyard's database.
var databaseCmd = &cobra.Command{
	Use:   "database",
	Short: "Database subcommand migrate/backup/restore Dockyard's database.",
	Long:  ``,
}

// migrateDatabaseCmd is subcommand migrate Dockyard's database.
var migrateDatabaseCmd = &cobra.Command{
	Use:   "migrate",
	Short: "migrate subcommand migrate Dockyard's database.",
	Long:  ``,
	Run:   migrateDatabase,
}

// backupDatabaseCmd is subcommand backup Dockyard's database.
var backupDatabaseCmd = &cobra.Command{
	Use:   "backup",
	Short: "backup subcommand backup Dockyard's database.",
	Long:  ``,
	Run:   backupDatabase,
}

// restoreDatabaseCmd is subcommand restore Dockyard's database.
var restoreDatabaseCmd = &cobra.Command{
	Use:   "restore",
	Short: "restore subcommand restore Dockyard's database.",
	Long:  ``,
	Run:   restoreDatabase,
}

// init()
func init() {
	RootCmd.AddCommand(databaseCmd)

	databaseCmd.AddCommand(migrateDatabaseCmd)
	databaseCmd.AddCommand(backupDatabaseCmd)
	databaseCmd.AddCommand(restoreDatabaseCmd)
}

// migrateDatabase is auto-migrate database of Dockyard.
func migrateDatabase(cmd *cobra.Command, args []string) {
	model.Migrate()
}

// backupDatabase is
func backupDatabase(cmd *cobra.Command, args []string) {

}

// restoreDatabase is
func restoreDatabase(cmd *cobra.Command, args []string) {

}
<|endoftext|>