<commit_before>// Copyright 2016 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 translate

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"reflect"
	"strings"
	"testing"

	raw "cloud.google.com/go/translate/internal/translate/v2"
	"golang.org/x/net/context"
	"golang.org/x/text/language"
	"google.golang.org/api/option"
)

func initTest(ctx context.Context, t *testing.T) *Client {
	if testing.Short() {
		t.Skip("integration tests skipped in short mode")
	}
	apiKey := os.Getenv("GCLOUD_TESTS_API_KEY")
	if apiKey == "" {
		t.Skip("integration tests skipped: GCLOUD_TESTS_API_KEY not defined")
	}
	client, err := NewClient(ctx, option.WithAPIKey(apiKey))
	if err != nil {
		t.Fatalf("NewClient: %v", err)
	}
	return client
}

type fakeTransport struct {
	req *http.Request
}

func (t *fakeTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	t.req = req
	return &http.Response{
		Status:     fmt.Sprintf("%d OK", http.StatusOK),
		StatusCode: http.StatusOK,
		Body:       ioutil.NopCloser(strings.NewReader("{}")),
	}, nil
}

func TestTranslateURL(t *testing.T) {
	// The translate API has all inputs in the URL.
	// Make sure we generate the right one.
	ctx := context.Background()

	c, err := NewClient(ctx)
	if err != nil {
		t.Fatal(err)
	}
	// Replace HTTP client for testing.
	ft := &fakeTransport{}
	c.raw, err = raw.New(&http.Client{Transport: ft})
	if err != nil {
		t.Fatal(err)
	}
	for _, test := range []struct {
		target language.Tag
		inputs []string
		opts   *Options
		want   url.Values
	}{
		{language.Spanish, []string{"text"}, nil, url.Values{
			"q":      []string{"text"},
			"target": []string{"es"},
		}},
		{language.English, []string{"text"}, &Options{}, url.Values{
			"q":      []string{"text"},
			"target": []string{"en"},
		}},
		{language.Turkish, []string{"t1", "t2"}, nil, url.Values{
			"q":      []string{"t1", "t2"},
			"target": []string{"tr"},
		}},
		{language.English, []string{"text"}, &Options{Source: language.French},
			url.Values{
				"q":      []string{"text"},
				"source": []string{"fr"},
				"target": []string{"en"},
			},
		},
		{language.English, []string{"text"}, &Options{Source: language.French, Format: HTML}, url.Values{
			"q":      []string{"text"},
			"source": []string{"fr"},
			"format": []string{"html"},
			"target": []string{"en"},
		}},
	} {
		_, err = c.Translate(ctx, test.inputs, test.target, test.opts)
		if err != nil {
			t.Fatal(err)
		}
		got := ft.req.URL.Query()
		test.want.Add("alt", "json")
		if !reflect.DeepEqual(got, test.want) {
			t.Errorf("Translate(%s, %v, %+v):\ngot  %s\nwant %s",
				test.target, test.inputs, test.opts, got, test.want)
		}
	}
}

func TestTranslateOneInput(t *testing.T) {
	ctx := context.Background()
	c := initTest(ctx, t)
	defer c.Close()

	translate := func(input string, target language.Tag, opts *Options) Translation {
		ts, err := c.Translate(ctx, []string{input}, target, opts)
		if err != nil {
			t.Fatal(err)
		}
		if len(ts) != 1 {
			t.Fatalf("wanted one Translation, got %d", len(ts))
		}
		return ts[0]
	}

	for _, test := range []struct {
		input  string
		source language.Tag
		output string
		target language.Tag
	}{
		// https://www.youtube.com/watch?v=x1sQkEfAdfY
		{"Le singe est sur la branche", language.French,
			"The monkey is on the branch", language.English},
		// https://www.youtube.com/watch?v=akbflkF_1zY
		{"I will not buy this record, it is scratched", language.English,
			"Nem fogok vásárolni ezt a lemezt, azt karcos", language.Hungarian},
	} {
		// Provide source and format.
		tr := translate(test.input, test.target, &Options{Source: test.source, Format: Text})
		if got, want := tr.Source, language.Und; got != want {
			t.Errorf("source: got %q, wanted %q", got, want)
			continue
		}
		if got, want := tr.Text, test.output; got != want {
			t.Errorf("text: got %q, want %q", got, want)
		}
		// Omit source; it should be detected.
		tr = translate(test.input, test.target, &Options{Format: Text})
		if got, want := tr.Source, test.source; got != want {
			t.Errorf("source: got %q, wanted %q", got, want)
			continue
		}
		if got, want := tr.Text, test.output; got != want {
			t.Errorf("text: got %q, want %q", got, want)
		}

		// Omit format. Defaults to HTML. Still works with plain text.
		tr = translate(test.input, test.target, nil)
		if got, want := tr.Source, test.source; got != want {
			t.Errorf("source: got %q, wanted %q", got, want)
			continue
		}
		if got, want := tr.Text, test.output; got != want {
			t.Errorf("text: got %q, want %q", got, want)
		}

		// Add HTML tags to input. They should be in output.
		htmlify := func(s string) string {
			return "" + s + ""
		}
		tr = translate(htmlify(test.input), test.target, nil)
		if got, want := tr.Text, htmlify(test.output); got != want {
			t.Errorf("html: got %q, want %q", got, want)
		}
		// Using the HTML format behaves the same.
		tr = translate(htmlify(test.input), test.target, &Options{Format: HTML})
		if got, want := tr.Text, htmlify(test.output); got != want {
			t.Errorf("html: got %q, want %q", got, want)
		}
	}
}

// This tests the beta "nmt" model.
func TestTranslateModel(t *testing.T) {
	ctx := context.Background()
	c := initTest(ctx, t)
	defer c.Close()

	trs, err := c.Translate(ctx, []string{"Hello"}, language.French, &Options{Model: "nmt"})
	if err != nil {
		t.Fatal(err)
	}
	if len(trs) != 1 {
		t.Fatalf("wanted one Translation, got %d", len(trs))
	}
	tr := trs[0]
	if got, want := tr.Text, "Bonjour"; got != want {
		t.Errorf("text: got %q, want %q", got, want)
	}
	if got, want := tr.Model, "nmt"; got != want {
		t.Errorf("model: got %q, want %q", got, want)
	}
}

func TestTranslateMultipleInputs(t *testing.T) {
	ctx := context.Background()
	c := initTest(ctx, t)
	defer c.Close()

	inputs := []string{
		"When you're a Jet, you're a Jet all the way",
		"From your first cigarette to your last dying day",
		"When you're a Jet if the spit hits the fan",
		"You got brothers around, you're a family man",
	}
	ts, err := c.Translate(ctx, inputs, language.French, nil)
	if err != nil {
		t.Fatal(err)
	}
	if got, want := len(ts), len(inputs); got != want {
		t.Fatalf("got %d Translations, wanted %d", got, want)
	}
}

func TestTranslateErrors(t *testing.T) {
	ctx := context.Background()
	c := initTest(ctx, t)
	defer c.Close()

	for _, test := range []struct {
		ctx    context.Context
		target language.Tag
		inputs []string
		opts   *Options
	}{
		{ctx, language.English, nil, nil},
		{ctx, language.Und, []string{"input"}, nil},
		{ctx, language.English, []string{}, nil},
		{ctx, language.English, []string{"input"}, &Options{Format: "random"}},
	} {
		_, err := c.Translate(test.ctx, test.inputs, test.target, test.opts)
		if err == nil {
			t.Errorf("%+v: got nil, want error", test)
		}
	}
}

func TestDetectLanguage(t *testing.T) {
	ctx := context.Background()
	c := initTest(ctx, t)
	defer c.Close()
	ds, err := c.DetectLanguage(ctx, []string{
		"Today is Monday",
		"Aujourd'hui est lundi",
	})
	if err != nil {
		t.Fatal(err)
	}
	if len(ds) != 2 {
		t.Fatalf("got %d detection lists, want 2", len(ds))
	}
	checkDetections(t, ds[0], language.English)
	checkDetections(t, ds[1], language.French)
}

func checkDetections(t *testing.T, ds []Detection, want language.Tag) {
	for _, d := range ds {
		if d.Language == want {
			return
		}
	}
	t.Errorf("%v: missing %s", ds, want)
}

// A small subset of the supported languages.
var supportedLangs = []Language{
	{Name: "Danish", Tag: language.Danish},
	{Name: "English", Tag: language.English},
	{Name: "French", Tag: language.French},
	{Name: "German", Tag: language.German},
	{Name: "Greek", Tag: language.Greek},
	{Name: "Hindi", Tag: language.Hindi},
	{Name: "Hungarian", Tag: language.Hungarian},
	{Name: "Italian", Tag: language.Italian},
	{Name: "Russian", Tag: language.Russian},
	{Name: "Turkish", Tag: language.Turkish},
}

func TestSupportedLanguages(t *testing.T) {
	ctx := context.Background()
	c := initTest(ctx, t)
	defer c.Close()
	got, err := c.SupportedLanguages(ctx, language.English)
	if err != nil {
		t.Fatal(err)
	}
	want := map[language.Tag]Language{}
	for _, sl := range supportedLangs {
		want[sl.Tag] = sl
	}
	for _, g := range got {
		w, ok := want[g.Tag]
		if !ok {
			continue
		}
		if g != w {
			t.Errorf("got %+v, want %+v", g, w)
		}
		delete(want, g.Tag)
	}
	if len(want) > 0 {
		t.Errorf("missing: %+v", want)
	}
}
<commit_msg>translate: fix broken TranslateURL test<commit_after>// Copyright 2016 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 translate

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"reflect"
	"strings"
	"testing"

	"golang.org/x/net/context"
	"golang.org/x/text/language"
	"google.golang.org/api/option"
)

func initTest(ctx context.Context, t *testing.T) *Client {
	if testing.Short() {
		t.Skip("integration tests skipped in short mode")
	}
	apiKey := os.Getenv("GCLOUD_TESTS_API_KEY")
	if apiKey == "" {
		t.Skip("integration tests skipped: GCLOUD_TESTS_API_KEY not defined")
	}
	client, err := NewClient(ctx, option.WithAPIKey(apiKey))
	if err != nil {
		t.Fatalf("NewClient: %v", err)
	}
	return client
}

type fakeTransport struct {
	req *http.Request
}

func (t *fakeTransport) RoundTrip(req *http.Request) (*http.Response, error) {
	t.req = req
	return &http.Response{
		Status:     fmt.Sprintf("%d OK", http.StatusOK),
		StatusCode: http.StatusOK,
		Body:       ioutil.NopCloser(strings.NewReader("{}")),
	}, nil
}

func TestTranslateURL(t *testing.T) {
	// The translate API has all inputs in the URL.
	// Make sure we generate the right one.
	ctx := context.Background()
	ft := &fakeTransport{}
	c, err := NewClient(ctx, option.WithHTTPClient(&http.Client{Transport: ft}))
	if err != nil {
		t.Fatal(err)
	}
	for _, test := range []struct {
		target language.Tag
		inputs []string
		opts   *Options
		want   url.Values
	}{
		{language.Spanish, []string{"text"}, nil, url.Values{
			"q":      []string{"text"},
			"target": []string{"es"},
		}},
		{language.English, []string{"text"}, &Options{}, url.Values{
			"q":      []string{"text"},
			"target": []string{"en"},
		}},
		{language.Turkish, []string{"t1", "t2"}, nil, url.Values{
			"q":      []string{"t1", "t2"},
			"target": []string{"tr"},
		}},
		{language.English, []string{"text"}, &Options{Source: language.French},
			url.Values{
				"q":      []string{"text"},
				"source": []string{"fr"},
				"target": []string{"en"},
			},
		},
		{language.English, []string{"text"}, &Options{Source: language.French, Format: HTML}, url.Values{
			"q":      []string{"text"},
			"source": []string{"fr"},
			"format": []string{"html"},
			"target": []string{"en"},
		}},
	} {
		_, err = c.Translate(ctx, test.inputs, test.target, test.opts)
		if err != nil {
			t.Fatal(err)
		}
		got := ft.req.URL.Query()
		test.want.Add("alt", "json")
		if !reflect.DeepEqual(got, test.want) {
			t.Errorf("Translate(%s, %v, %+v):\ngot  %s\nwant %s",
				test.target, test.inputs, test.opts, got, test.want)
		}
	}
}

func TestTranslateOneInput(t *testing.T) {
	ctx := context.Background()
	c := initTest(ctx, t)
	defer c.Close()

	translate := func(input string, target language.Tag, opts *Options) Translation {
		ts, err := c.Translate(ctx, []string{input}, target, opts)
		if err != nil {
			t.Fatal(err)
		}
		if len(ts) != 1 {
			t.Fatalf("wanted one Translation, got %d", len(ts))
		}
		return ts[0]
	}

	for _, test := range []struct {
		input  string
		source language.Tag
		output string
		target language.Tag
	}{
		// https://www.youtube.com/watch?v=x1sQkEfAdfY
		{"Le singe est sur la branche", language.French,
			"The monkey is on the branch", language.English},
		// https://www.youtube.com/watch?v=akbflkF_1zY
		{"I will not buy this record, it is scratched", language.English,
			"Nem fogok vásárolni ezt a lemezt, azt karcos", language.Hungarian},
	} {
		// Provide source and format.
		tr := translate(test.input, test.target, &Options{Source: test.source, Format: Text})
		if got, want := tr.Source, language.Und; got != want {
			t.Errorf("source: got %q, wanted %q", got, want)
			continue
		}
		if got, want := tr.Text, test.output; got != want {
			t.Errorf("text: got %q, want %q", got, want)
		}
		// Omit source; it should be detected.
		tr = translate(test.input, test.target, &Options{Format: Text})
		if got, want := tr.Source, test.source; got != want {
			t.Errorf("source: got %q, wanted %q", got, want)
			continue
		}
		if got, want := tr.Text, test.output; got != want {
			t.Errorf("text: got %q, want %q", got, want)
		}

		// Omit format. Defaults to HTML. Still works with plain text.
		tr = translate(test.input, test.target, nil)
		if got, want := tr.Source, test.source; got != want {
			t.Errorf("source: got %q, wanted %q", got, want)
			continue
		}
		if got, want := tr.Text, test.output; got != want {
			t.Errorf("text: got %q, want %q", got, want)
		}

		// Add HTML tags to input. They should be in output.
		htmlify := func(s string) string {
			return "" + s + ""
		}
		tr = translate(htmlify(test.input), test.target, nil)
		if got, want := tr.Text, htmlify(test.output); got != want {
			t.Errorf("html: got %q, want %q", got, want)
		}
		// Using the HTML format behaves the same.
		tr = translate(htmlify(test.input), test.target, &Options{Format: HTML})
		if got, want := tr.Text, htmlify(test.output); got != want {
			t.Errorf("html: got %q, want %q", got, want)
		}
	}
}

// This tests the beta "nmt" model.
func TestTranslateModel(t *testing.T) {
	ctx := context.Background()
	c := initTest(ctx, t)
	defer c.Close()

	trs, err := c.Translate(ctx, []string{"Hello"}, language.French, &Options{Model: "nmt"})
	if err != nil {
		t.Fatal(err)
	}
	if len(trs) != 1 {
		t.Fatalf("wanted one Translation, got %d", len(trs))
	}
	tr := trs[0]
	if got, want := tr.Text, "Bonjour"; got != want {
		t.Errorf("text: got %q, want %q", got, want)
	}
	if got, want := tr.Model, "nmt"; got != want {
		t.Errorf("model: got %q, want %q", got, want)
	}
}

func TestTranslateMultipleInputs(t *testing.T) {
	ctx := context.Background()
	c := initTest(ctx, t)
	defer c.Close()

	inputs := []string{
		"When you're a Jet, you're a Jet all the way",
		"From your first cigarette to your last dying day",
		"When you're a Jet if the spit hits the fan",
		"You got brothers around, you're a family man",
	}
	ts, err := c.Translate(ctx, inputs, language.French, nil)
	if err != nil {
		t.Fatal(err)
	}
	if got, want := len(ts), len(inputs); got != want {
		t.Fatalf("got %d Translations, wanted %d", got, want)
	}
}

func TestTranslateErrors(t *testing.T) {
	ctx := context.Background()
	c := initTest(ctx, t)
	defer c.Close()

	for _, test := range []struct {
		ctx    context.Context
		target language.Tag
		inputs []string
		opts   *Options
	}{
		{ctx, language.English, nil, nil},
		{ctx, language.Und, []string{"input"}, nil},
		{ctx, language.English, []string{}, nil},
		{ctx, language.English, []string{"input"}, &Options{Format: "random"}},
	} {
		_, err := c.Translate(test.ctx, test.inputs, test.target, test.opts)
		if err == nil {
			t.Errorf("%+v: got nil, want error", test)
		}
	}
}

func TestDetectLanguage(t *testing.T) {
	ctx := context.Background()
	c := initTest(ctx, t)
	defer c.Close()
	ds, err := c.DetectLanguage(ctx, []string{
		"Today is Monday",
		"Aujourd'hui est lundi",
	})
	if err != nil {
		t.Fatal(err)
	}
	if len(ds) != 2 {
		t.Fatalf("got %d detection lists, want 2", len(ds))
	}
	checkDetections(t, ds[0], language.English)
	checkDetections(t, ds[1], language.French)
}

func checkDetections(t *testing.T, ds []Detection, want language.Tag) {
	for _, d := range ds {
		if d.Language == want {
			return
		}
	}
	t.Errorf("%v: missing %s", ds, want)
}

// A small subset of the supported languages.
var supportedLangs = []Language{
	{Name: "Danish", Tag: language.Danish},
	{Name: "English", Tag: language.English},
	{Name: "French", Tag: language.French},
	{Name: "German", Tag: language.German},
	{Name: "Greek", Tag: language.Greek},
	{Name: "Hindi", Tag: language.Hindi},
	{Name: "Hungarian", Tag: language.Hungarian},
	{Name: "Italian", Tag: language.Italian},
	{Name: "Russian", Tag: language.Russian},
	{Name: "Turkish", Tag: language.Turkish},
}

func TestSupportedLanguages(t *testing.T) {
	ctx := context.Background()
	c := initTest(ctx, t)
	defer c.Close()
	got, err := c.SupportedLanguages(ctx, language.English)
	if err != nil {
		t.Fatal(err)
	}
	want := map[language.Tag]Language{}
	for _, sl := range supportedLangs {
		want[sl.Tag] = sl
	}
	for _, g := range got {
		w, ok := want[g.Tag]
		if !ok {
			continue
		}
		if g != w {
			t.Errorf("got %+v, want %+v", g, w)
		}
		delete(want, g.Tag)
	}
	if len(want) > 0 {
		t.Errorf("missing: %+v", want)
	}
}
<|endoftext|>


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

package main

import (
	"bytes"
	"fmt"
	"strings"

	"github.com/spf13/cobra"
	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/runtime/serializer"
	"k8s.io/client-go/kubernetes/scheme"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/tools/remotecommand"

	"istio.io/istio/pkg/log"
)

var (

	// TODO - Pull in remaining xDS information from pilot agent via curl and add to output
	// TODO - Add config-diff to get the difference between pilot's xDS API response and the proxy config
	// TODO - Add support for non-default proxy config locations
	// TODO - Add support for non-kube istio deployments
	configCmd = &cobra.Command{
		Use:   "proxy-config <pod-name>",
		Short: "Retrieves proxy configuration for the specified pod [kube only]",
		Long: `
Retrieves the static/bootstrap proxy configuration for the specified pod when running in Kubernetes.
Support for other environments to follow.
`,
		Example: `  # Retrieve config for productpage-v1-bb8d5cbc7-k7qbm pod
  istioctl proxy-config productpage-v1-bb8d5cbc7-k7qbm`,
		Aliases: []string{"pc"},
		Args:    cobra.MinimumNArgs(1),
		RunE: func(c *cobra.Command, args []string) error {
			podName := args[0]
			log.Infof("Retrieving proxy config for %q", podName)

			ns := namespace
			if ns == v1.NamespaceAll {
				ns = defaultNamespace
			}
			config, err := readConfigFile(podName, ns)
			if err != nil {
				return err
			}

			fmt.Println(config)

			return nil
		},
	}
)

func init() {
	rootCmd.AddCommand(configCmd)
}

func createCoreV1Client() (*rest.RESTClient, error) {
	config, err := defaultRestConfig()
	if err != nil {
		return nil, err
	}
	return rest.RESTClientFor(config)
}

func defaultRestConfig() (*rest.Config, error) {
	config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
	if err != nil {
		return nil, err
	}
	config.APIPath = "/api"
	config.GroupVersion = &v1.SchemeGroupVersion
	config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
	return config, nil
}

func readConfigFile(podName, podNamespace string) (string, error) {
	// Get filename to read from
	var fileLocation string
	cmd := []string{"ls", "-Art", "/etc/istio/proxy"}
	if stdout, stderr, err := podExec(podName, podNamespace, cmd); err != nil {
		return "", err
	} else if stderr.String() != "" {
		return "", fmt.Errorf("unable to find config file: %v", stderr.String())
	} else {
		// Use the first file in the sorted ls
		resp := strings.Fields(stdout.String())
		fileLocation = fmt.Sprintf("/etc/istio/proxy/%v", resp[0])
	}

	// Cat the file
	cmd = []string{"cat", fileLocation}
	if stdout, stderr, err := podExec(podName, podNamespace, cmd); err != nil {
		return "", err
	} else if stderr.String() != "" {
		return "", fmt.Errorf("unable to read config file: %v", stderr.String())
	} else {
		return stdout.String(), nil
	}
}

func podExec(podName, podNamespace string, command []string) (*bytes.Buffer, *bytes.Buffer, error) {
	client, err := createCoreV1Client()
	if err != nil {
		return nil, nil, err
	}

	req := client.Post().
		Resource("pods").
		Name(podName).
		Namespace(podNamespace).
		SubResource("exec").
		Param("container", "istio-proxy").
		VersionedParams(&v1.PodExecOptions{
			Container: "istio-proxy",
			Command:   command,
			Stdin:     false,
			Stdout:    true,
			Stderr:    true,
			TTY:       false,
		}, scheme.ParameterCodec)

	config, err := defaultRestConfig()
	if err != nil {
		return nil, nil, err
	}

	exec, err := remotecommand.NewSPDYExecutor(config, "POST", req.URL())
	if err != nil {
		return nil, nil, err
	}

	var stdout, stderr bytes.Buffer
	err = exec.Stream(remotecommand.StreamOptions{
		Stdin:  nil,
		Stdout: &stdout,
		Stderr: &stderr,
		Tty:    false,
	})

	return &stdout, &stderr, err
}
<commit_msg>Fix example spacing that was leading to odd format on istio.io (#3305)<commit_after>// Copyright 2018 Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"bytes"
	"fmt"
	"strings"

	"github.com/spf13/cobra"
	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/runtime/serializer"
	"k8s.io/client-go/kubernetes/scheme"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/tools/remotecommand"

	"istio.io/istio/pkg/log"
)

var (

	// TODO - Pull in remaining xDS information from pilot agent via curl and add to output
	// TODO - Add config-diff to get the difference between pilot's xDS API response and the proxy config
	// TODO - Add support for non-default proxy config locations
	// TODO - Add support for non-kube istio deployments
	configCmd = &cobra.Command{
		Use:   "proxy-config <pod-name>",
		Short: "Retrieves proxy configuration for the specified pod [kube only]",
		Long: `
Retrieves the static/bootstrap proxy configuration for the specified pod when running in Kubernetes.
Support for other environments to follow.
`,
		Example: `# Retrieve config for productpage-v1-bb8d5cbc7-k7qbm pod
istioctl proxy-config productpage-v1-bb8d5cbc7-k7qbm`,
		Aliases: []string{"pc"},
		Args:    cobra.MinimumNArgs(1),
		RunE: func(c *cobra.Command, args []string) error {
			podName := args[0]
			log.Infof("Retrieving proxy config for %q", podName)

			ns := namespace
			if ns == v1.NamespaceAll {
				ns = defaultNamespace
			}
			config, err := readConfigFile(podName, ns)
			if err != nil {
				return err
			}

			fmt.Println(config)

			return nil
		},
	}
)

func init() {
	rootCmd.AddCommand(configCmd)
}

func createCoreV1Client() (*rest.RESTClient, error) {
	config, err := defaultRestConfig()
	if err != nil {
		return nil, err
	}
	return rest.RESTClientFor(config)
}

func defaultRestConfig() (*rest.Config, error) {
	config, err := clientcmd.BuildConfigFromFlags("", kubeconfig)
	if err != nil {
		return nil, err
	}
	config.APIPath = "/api"
	config.GroupVersion = &v1.SchemeGroupVersion
	config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
	return config, nil
}

func readConfigFile(podName, podNamespace string) (string, error) {
	// Get filename to read from
	var fileLocation string
	cmd := []string{"ls", "-Art", "/etc/istio/proxy"}
	if stdout, stderr, err := podExec(podName, podNamespace, cmd); err != nil {
		return "", err
	} else if stderr.String() != "" {
		return "", fmt.Errorf("unable to find config file: %v", stderr.String())
	} else {
		// Use the first file in the sorted ls
		resp := strings.Fields(stdout.String())
		fileLocation = fmt.Sprintf("/etc/istio/proxy/%v", resp[0])
	}

	// Cat the file
	cmd = []string{"cat", fileLocation}
	if stdout, stderr, err := podExec(podName, podNamespace, cmd); err != nil {
		return "", err
	} else if stderr.String() != "" {
		return "", fmt.Errorf("unable to read config file: %v", stderr.String())
	} else {
		return stdout.String(), nil
	}
}

func podExec(podName, podNamespace string, command []string) (*bytes.Buffer, *bytes.Buffer, error) {
	client, err := createCoreV1Client()
	if err != nil {
		return nil, nil, err
	}

	req := client.Post().
		Resource("pods").
		Name(podName).
		Namespace(podNamespace).
		SubResource("exec").
		Param("container", "istio-proxy").
		VersionedParams(&v1.PodExecOptions{
			Container: "istio-proxy",
			Command:   command,
			Stdin:     false,
			Stdout:    true,
			Stderr:    true,
			TTY:       false,
		}, scheme.ParameterCodec)

	config, err := defaultRestConfig()
	if err != nil {
		return nil, nil, err
	}

	exec, err := remotecommand.NewSPDYExecutor(config, "POST", req.URL())
	if err != nil {
		return nil, nil, err
	}

	var stdout, stderr bytes.Buffer
	err = exec.Stream(remotecommand.StreamOptions{
		Stdin:  nil,
		Stdout: &stdout,
		Stderr: &stderr,
		Tty:    false,
	})

	return &stdout, &stderr, err
}
<|endoftext|>


<commit_before>package clideployment

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

	"git.containerum.net/ch/kube-client/pkg/model"
	"github.com/containerum/chkit/pkg/context"
	"github.com/containerum/chkit/pkg/model/container"
	"github.com/containerum/chkit/pkg/model/deployment"
	"github.com/containerum/chkit/pkg/model/deployment/deplactive"
	"github.com/containerum/chkit/pkg/util/activekit"
	"github.com/containerum/chkit/pkg/util/angel"
	"github.com/containerum/chkit/pkg/util/namegen"
	"github.com/containerum/chkit/pkg/util/pairs"
	"github.com/containerum/chkit/pkg/util/text"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
)

func Create(ctx *context.Context) *cobra.Command {
	var file string
	var force bool
	var flagCont container.Container
	var flagDepl deployment.Deployment
	var envs string
	command := &cobra.Command{
		Use:     "deployment",
		Aliases: aliases,
		Short:   "create new deployment",
		Long: `Creates new deployment.
Has an one-line mode, suitable for integration with other tools, and an interactive wizard mode`,
		Run: func(cmd *cobra.Command, args []string) {
			depl := deplactive.DefaultDeployment()
			if cmd.Flag("file").Changed {
				var err error
				depl, err = deplactive.FromFile(file)
				if err != nil {
					logrus.WithError(err).Errorf("unable to load deployment data from file %s", file)
					fmt.Printf("Unable to load deployment data from file :(\n%v", err)
					os.Exit(1)
				}
			} else if cmd.Flag("force").Changed {
				if cmd.Flag("env").Changed {
					envMap, err := pairs.ParseMap(envs, ":")
					if err != nil {
						fmt.Printf("invalid env flag\n")
						os.Exit(1)
					}
					for k, v := range envMap {
						flagCont.Env = append(flagCont.Env, model.Env{
							Name:  k,
							Value: v,
						})
					}
				}
				if flagCont.Name == "" {
					flagCont.Name = namegen.Aster() + "-" + flagCont.Image
				}
				flagDepl.Containers = []container.Container{flagCont}
				depl = flagDepl
			}
			if cmd.Flag("force").Changed {
				if err := deplactive.ValidateDeployment(depl); err != nil {
					fmt.Println(err)
					os.Exit(1)
				}
				fmt.Println(depl.RenderTable())
				if err := ctx.Client.CreateDeployment(ctx.Namespace, depl); err != nil {
					fmt.Println(err)
					os.Exit(1)
				}
				fmt.Println("OK")
				return
			}
			depl, err := deplactive.Wizard(deplactive.Config{
				Deployment: &depl,
			})
			if err != nil {
				logrus.WithError(err).Errorf("unable to create deployment")
				fmt.Println(err)
				os.Exit(1)
			}
			for {
				_, err := (&activekit.Menu{
					Items: []*activekit.MenuItem{
						{
							Label: "Push deployment to server",
							Action: func() error {
								err := ctx.Client.CreateDeployment(ctx.Namespace, depl)
								if err != nil {
									logrus.WithError(err).Errorf("unable to create deployment %q", depl.Name)
									fmt.Println(err)
									return nil
								}
								fmt.Printf("Congratulations! Deployment %q created!\n", depl.Name)
								return nil
							},
						},
						{
							Label: "Edit deployment",
							Action: func() error {
								var err error
								depl, err = deplactive.Wizard(deplactive.Config{
									Deployment: &depl,
								})
								if err != nil {
									logrus.WithError(err).Errorf("unable to create deployment")
									fmt.Println(err)
									os.Exit(1)
								}
								return nil
							},
						},
						{
							Label: "Print to terminal",
							Action: activekit.ActionWithErr(func() error {
								if data, err := depl.RenderYAML(); err != nil {
									return err
								} else {
									upBorders := strings.Repeat("_", text.Width(data))
									downBorders := strings.Repeat("_", text.Width(data))
									fmt.Printf("%s\n\n%s\n%s\n", upBorders, data, downBorders)
								}
								return nil
							}),
						},
						{
							Label: "Save to file",
							Action: func() error {
								filename, _ := activekit.AskLine("Print filename: ")
								data, err := depl.RenderJSON()
								if err != nil {
									return err
								}
								if err := ioutil.WriteFile(filename, []byte(data), os.ModePerm); err != nil {
									logrus.WithError(err).Errorf("unable to save deployment %q to file", depl.Name)
									fmt.Printf("Unable to save deployment to file :(\n%v", err)
									return nil
								}
								fmt.Printf("OK\n")
								return nil
							},
						},
						{
							Label: "Exit",
							Action: func() error {
								if yes, _ := activekit.Yes("Are you sure you want to exit?"); yes {
									os.Exit(0)
								}
								return nil
							},
						},
					},
				}).Run()
				if err != nil {
					logrus.WithError(err).Errorf("error while menu execution")
					angel.Angel(ctx, err)
					os.Exit(1)
				}
			}
		},
	}
	command.PersistentFlags().
		StringVar(&file, "file", "", "create deployment from file")
	command.PersistentFlags().
		BoolVarP(&force, "force", "f", false, "suppress confirmation")

	command.PersistentFlags().
		StringVar(&flagDepl.Name, "deployment-name", namegen.Color()+"-"+namegen.Aster(), "deployment name, optional")
	command.PersistentFlags().
		IntVar(&flagDepl.Replicas, "replicas", 1, "replicas, optional")
	command.PersistentFlags().
		StringVar(&flagCont.Name, "container-name", "", "container name, equal to image name by default")
	command.PersistentFlags().
		StringVar(&flagCont.Image, "image", "", "container image, required")
	command.PersistentFlags().
		UintVar(&flagCont.Limits.Memory, "memory", 256, "container memory limit im Mb, optional")
	command.PersistentFlags().
		UintVar(&flagCont.Limits.CPU, "cpu", 200, "container CPU limit in mCPU, optional")
	command.PersistentFlags().
		StringSliceVar(&flagCont.Commands, "commands", nil, "container commands")
	command.PersistentFlags().
		StringVar(&envs, "env", "", "container env variable in KEY0:VALUE0 KEY1:VALUE1 format")
	return command
}
<commit_msg>refactor tui<commit_after>package clideployment

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

	"git.containerum.net/ch/kube-client/pkg/model"
	"github.com/containerum/chkit/pkg/context"
	"github.com/containerum/chkit/pkg/model/container"
	"github.com/containerum/chkit/pkg/model/deployment"
	"github.com/containerum/chkit/pkg/model/deployment/deplactive"
	"github.com/containerum/chkit/pkg/util/activekit"
	"github.com/containerum/chkit/pkg/util/angel"
	"github.com/containerum/chkit/pkg/util/namegen"
	"github.com/containerum/chkit/pkg/util/pairs"
	"github.com/containerum/chkit/pkg/util/text"
	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
)

func Create(ctx *context.Context) *cobra.Command {
	var file string
	var force bool
	var flagCont container.Container
	var flagDepl deployment.Deployment
	var envs string
	command := &cobra.Command{
		Use:     "deployment",
		Aliases: aliases,
		Short:   "create new deployment",
		Long: `Creates new deployment.
Has an one-line mode, suitable for integration with other tools, and an interactive wizard mode`,
		Run: func(cmd *cobra.Command, args []string) {
			depl := deplactive.DefaultDeployment()
			if cmd.Flag("file").Changed {
				var err error
				depl, err = deplactive.FromFile(file)
				if err != nil {
					logrus.WithError(err).Errorf("unable to load deployment data from file %s", file)
					fmt.Printf("Unable to load deployment data from file :(\n%v", err)
					os.Exit(1)
				}
			} else if cmd.Flag("force").Changed {
				if cmd.Flag("env").Changed {
					envMap, err := pairs.ParseMap(envs, ":")
					if err != nil {
						fmt.Printf("invalid env flag\n")
						os.Exit(1)
					}
					for k, v := range envMap {
						flagCont.Env = append(flagCont.Env, model.Env{
							Name:  k,
							Value: v,
						})
					}
				}
				if flagCont.Name == "" {
					flagCont.Name = namegen.Aster() + "-" + flagCont.Image
				}
				flagDepl.Containers = []container.Container{flagCont}
				depl = flagDepl
			}
			if cmd.Flag("force").Changed {
				if err := deplactive.ValidateDeployment(depl); err != nil {
					fmt.Println(err)
					os.Exit(1)
				}
				fmt.Println(depl.RenderTable())
				if err := ctx.Client.CreateDeployment(ctx.Namespace, depl); err != nil {
					fmt.Println(err)
					os.Exit(1)
				}
				fmt.Println("OK")
				return
			}
			depl, err := deplactive.Wizard(deplactive.Config{
				Deployment: &depl,
			})
			if err != nil {
				logrus.WithError(err).Errorf("unable to create deployment")
				fmt.Println(err)
				os.Exit(1)
			}
			if activekit.YesNo("Are you sure?") {
				if err := ctx.Client.CreateDeployment(ctx.Namespace, depl); err != nil {
					logrus.WithError(err).Errorf("unable to create deployment %q", depl.Name)
					activekit.Attention(err.Error())
					os.Exit(1)
				}
				fmt.Printf("Congratulations! Deployment %q created!\n", depl.Name)
			}
			for {
				_, err := (&activekit.Menu{
					Items: []*activekit.MenuItem{
						{
							Label: "Push changes to server",
							Action: func() error {
								if activekit.YesNo("Are you sure?") {
									err := ctx.Client.ReplaceDeployment(ctx.Namespace, depl)
									if err != nil {
										logrus.WithError(err).Errorf("unable to update deployment %q", depl.Name)
										fmt.Println(err)
										return nil
									}
									fmt.Printf("Congratulations! Deployment %q updated!\n", depl.Name)
								}
								return nil
							},
						},
						{
							Label: "Edit deployment",
							Action: func() error {
								var err error
								depl, err = deplactive.ReplaceWizard(deplactive.Config{
									Deployment: &depl,
								})
								if err != nil {
									logrus.WithError(err).Errorf("unable to create deployment")
									fmt.Println(err)
									os.Exit(1)
								}
								return nil
							},
						},
						{
							Label: "Print to terminal",
							Action: activekit.ActionWithErr(func() error {
								if data, err := depl.RenderYAML(); err != nil {
									return err
								} else {
									upBorders := strings.Repeat("_", text.Width(data))
									downBorders := strings.Repeat("_", text.Width(data))
									fmt.Printf("%s\n\n%s\n%s\n", upBorders, data, downBorders)
								}
								return nil
							}),
						},
						{
							Label: "Save to file",
							Action: func() error {
								filename, _ := activekit.AskLine("Print filename: ")
								data, err := depl.RenderJSON()
								if err != nil {
									return err
								}
								if err := ioutil.WriteFile(filename, []byte(data), os.ModePerm); err != nil {
									logrus.WithError(err).Errorf("unable to save deployment %q to file", depl.Name)
									fmt.Printf("Unable to save deployment to file :(\n%v", err)
									return nil
								}
								fmt.Printf("OK\n")
								return nil
							},
						},
						{
							Label: "Exit",
							Action: func() error {
								if yes, _ := activekit.Yes("Are you sure you want to exit?"); yes {
									os.Exit(0)
								}
								return nil
							},
						},
					},
				}).Run()
				if err != nil {
					logrus.WithError(err).Errorf("error while menu execution")
					angel.Angel(ctx, err)
					os.Exit(1)
				}
			}
		},
	}
	command.PersistentFlags().
		StringVar(&file, "file", "", "create deployment from file")
	command.PersistentFlags().
		BoolVarP(&force, "force", "f", false, "suppress confirmation")

	command.PersistentFlags().
		StringVar(&flagDepl.Name, "deployment-name", namegen.Color()+"-"+namegen.Aster(), "deployment name, optional")
	command.PersistentFlags().
		IntVar(&flagDepl.Replicas, "replicas", 1, "replicas, optional")
	command.PersistentFlags().
		StringVar(&flagCont.Name, "container-name", "", "container name, equal to image name by default")
	command.PersistentFlags().
		StringVar(&flagCont.Image, "image", "", "container image, required")
	command.PersistentFlags().
		UintVar(&flagCont.Limits.Memory, "memory", 256, "container memory limit im Mb, optional")
	command.PersistentFlags().
		UintVar(&flagCont.Limits.CPU, "cpu", 200, "container CPU limit in mCPU, optional")
	command.PersistentFlags().
		StringSliceVar(&flagCont.Commands, "commands", nil, "container commands")
	command.PersistentFlags().
		StringVar(&envs, "env", "", "container env variable in KEY0:VALUE0 KEY1:VALUE1 format")
	return command
}
<|endoftext|>


<commit_before>package http

import (
	"bytes"
	"fmt"
	"github.com/Aptomi/aptomi/pkg/api"
	"github.com/Aptomi/aptomi/pkg/api/codec"
	"github.com/Aptomi/aptomi/pkg/config"
	"github.com/Aptomi/aptomi/pkg/runtime"
	"io"
	"io/ioutil"
	"net/http"
)

// Client is the interface for doing HTTP requests that operates using runtime objects
type Client interface {
	GET(path string, expected *runtime.Info) (runtime.Object, error)
	POST(path string, expected *runtime.Info, body runtime.Object) (runtime.Object, error)
	POSTSlice(path string, expected *runtime.Info, body []runtime.Object) (runtime.Object, error)
	DELETESlice(path string, expected *runtime.Info, body []runtime.Object) (runtime.Object, error)
}

type httpClient struct {
	contentType *codec.ContentTypeHandler
	http        *http.Client
	cfg         *config.Client
}

// NewClient returns implementation of
func NewClient(cfg *config.Client) Client {
	client := &http.Client{
		Timeout: cfg.HTTP.Timeout,
	}
	contentTypeHandler := codec.NewContentTypeHandler(runtime.NewRegistry().Append(api.Objects...))

	return &httpClient{contentTypeHandler, client, cfg}
}

func (client *httpClient) GET(path string, expected *runtime.Info) (runtime.Object, error) {
	return client.request(http.MethodGet, path, expected, nil)
}

func (client *httpClient) POST(path string, expected *runtime.Info, body runtime.Object) (runtime.Object, error) {
	var bodyData io.Reader

	if body != nil {
		data, err := client.contentType.GetCodecByContentType(codec.Default).EncodeOne(body)
		if err != nil {
			return nil, fmt.Errorf("error while encoding body for post request: %s", err)
		}
		bodyData = bytes.NewBuffer(data)
	}

	return client.request(http.MethodPost, path, expected, bodyData)
}

func (client *httpClient) POSTSlice(path string, expected *runtime.Info, body []runtime.Object) (runtime.Object, error) {
	var bodyData io.Reader

	if body != nil {
		data, err := client.contentType.GetCodecByContentType(codec.Default).EncodeMany(body)
		if err != nil {
			return nil, fmt.Errorf("error while encoding body for post request: %s", err)
		}
		bodyData = bytes.NewBuffer(data)
	}

	return client.request(http.MethodPost, path, expected, bodyData)
}

func (client *httpClient) DELETESlice(path string, expected *runtime.Info, body []runtime.Object) (runtime.Object, error) {
	var bodyData io.Reader

	if body != nil {
		data, err := client.contentType.GetCodecByContentType(codec.Default).EncodeMany(body)
		if err != nil {
			return nil, fmt.Errorf("error while encoding body for delete request: %s", err)
		}
		bodyData = bytes.NewBuffer(data)
	}

	return client.request(http.MethodDelete, path, expected, bodyData)
}

func (client *httpClient) request(method string, path string, expected *runtime.Info, body io.Reader) (runtime.Object, error) {
	req, err := http.NewRequest(method, client.cfg.API.URL()+path, body)
	if err != nil {
		return nil, err
	}

	if len(client.cfg.Auth.Username) > 0 {
		req.Header.Set("Username", client.cfg.Auth.Username)
	}
	req.Header.Set("Content-Type", codec.Default)
	req.Header.Set("User-Agent", "aptomictl")

	resp, err := client.http.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close() // nolint: errcheck

	respData, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("error while reading bytes from response Body: %s", err)
	}

	if len(respData) == 0 {
		return nil, fmt.Errorf("empty response")
	}

	obj, err := client.contentType.GetCodec(resp.Header).DecodeOne(respData)
	if err != nil {
		return nil, fmt.Errorf("error while unmarshalling response: %s", err)
	}

	if expected != nil && obj.GetKind() != expected.Kind {
		return nil, fmt.Errorf("received object kind %s doesn't match expected %s", obj.GetKind(), expected.Kind)
	}

	return obj, nil
}
<commit_msg>Handle server errors in client<commit_after>package http

import (
	"bytes"
	"fmt"
	"github.com/Aptomi/aptomi/pkg/api"
	"github.com/Aptomi/aptomi/pkg/api/codec"
	"github.com/Aptomi/aptomi/pkg/config"
	"github.com/Aptomi/aptomi/pkg/runtime"
	"io"
	"io/ioutil"
	"net/http"
)

// Client is the interface for doing HTTP requests that operates using runtime objects
type Client interface {
	GET(path string, expected *runtime.Info) (runtime.Object, error)
	POST(path string, expected *runtime.Info, body runtime.Object) (runtime.Object, error)
	POSTSlice(path string, expected *runtime.Info, body []runtime.Object) (runtime.Object, error)
	DELETESlice(path string, expected *runtime.Info, body []runtime.Object) (runtime.Object, error)
}

type httpClient struct {
	contentType *codec.ContentTypeHandler
	http        *http.Client
	cfg         *config.Client
}

// NewClient returns implementation of
func NewClient(cfg *config.Client) Client {
	client := &http.Client{
		Timeout: cfg.HTTP.Timeout,
	}
	contentTypeHandler := codec.NewContentTypeHandler(runtime.NewRegistry().Append(api.Objects...))

	return &httpClient{contentTypeHandler, client, cfg}
}

func (client *httpClient) GET(path string, expected *runtime.Info) (runtime.Object, error) {
	return client.request(http.MethodGet, path, expected, nil)
}

func (client *httpClient) POST(path string, expected *runtime.Info, body runtime.Object) (runtime.Object, error) {
	var bodyData io.Reader

	if body != nil {
		data, err := client.contentType.GetCodecByContentType(codec.Default).EncodeOne(body)
		if err != nil {
			return nil, fmt.Errorf("error while encoding body for post request: %s", err)
		}
		bodyData = bytes.NewBuffer(data)
	}

	return client.request(http.MethodPost, path, expected, bodyData)
}

func (client *httpClient) POSTSlice(path string, expected *runtime.Info, body []runtime.Object) (runtime.Object, error) {
	var bodyData io.Reader

	if body != nil {
		data, err := client.contentType.GetCodecByContentType(codec.Default).EncodeMany(body)
		if err != nil {
			return nil, fmt.Errorf("error while encoding body for post request: %s", err)
		}
		bodyData = bytes.NewBuffer(data)
	}

	return client.request(http.MethodPost, path, expected, bodyData)
}

func (client *httpClient) DELETESlice(path string, expected *runtime.Info, body []runtime.Object) (runtime.Object, error) {
	var bodyData io.Reader

	if body != nil {
		data, err := client.contentType.GetCodecByContentType(codec.Default).EncodeMany(body)
		if err != nil {
			return nil, fmt.Errorf("error while encoding body for delete request: %s", err)
		}
		bodyData = bytes.NewBuffer(data)
	}

	return client.request(http.MethodDelete, path, expected, bodyData)
}

func (client *httpClient) request(method string, path string, expected *runtime.Info, body io.Reader) (runtime.Object, error) {
	req, err := http.NewRequest(method, client.cfg.API.URL()+path, body)
	if err != nil {
		return nil, err
	}

	if len(client.cfg.Auth.Username) > 0 {
		req.Header.Set("Username", client.cfg.Auth.Username)
	}
	req.Header.Set("Content-Type", codec.Default)
	req.Header.Set("User-Agent", "aptomictl")

	resp, err := client.http.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close() // nolint: errcheck

	respData, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("error while reading bytes from response Body: %s", err)
	}

	if len(respData) == 0 {
		return nil, fmt.Errorf("empty response")
	}

	obj, err := client.contentType.GetCodec(resp.Header).DecodeOne(respData)
	if err != nil {
		return nil, fmt.Errorf("error while unmarshalling response: %s", err)
	}

	if obj.GetKind() == api.ServerErrorObject.Kind {
		serverErr, ok := obj.(*api.ServerError)
		if !ok {
			return nil, fmt.Errorf("server error, but it couldn't be casted to api.ServerError")
		}

		return nil, fmt.Errorf("server error: %s", serverErr.Error)
	}

	if expected != nil && obj.GetKind() != expected.Kind {
		return nil, fmt.Errorf("received object kind %s doesn't match expected %s", obj.GetKind(), expected.Kind)
	}

	return obj, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2016 Skippbox, Ltd.

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 controller

import (
	"errors"
	"fmt"
	"time"

	"github.com/Sirupsen/logrus"
	k8sErrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/pkg/api"
	"k8s.io/client-go/pkg/apis/extensions/v1beta1"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/util/workqueue"

	"github.com/kubeless/kubeless/pkg/spec"
	"github.com/kubeless/kubeless/pkg/utils"

	"k8s.io/apimachinery/pkg/types"
)

const (
	tprName    = "function.k8s.io"
	maxRetries = 5
)

var (
	errVersionOutdated = errors.New("Requested version is outdated in apiserver")
	initRetryWaitTime  = 30 * time.Second
)

// Controller object
type Controller struct {
	logger    *logrus.Entry
	clientset kubernetes.Interface
	tprclient rest.Interface
	Functions map[string]*spec.Function
	queue     workqueue.RateLimitingInterface
	informer  cache.SharedIndexInformer
}

// Config contains k8s client of a controller
type Config struct {
	KubeCli   kubernetes.Interface
	TprClient rest.Interface
}

// New initializes a controller object
func New(cfg Config) *Controller {
	lw := cache.NewListWatchFromClient(cfg.TprClient, "functions", api.NamespaceAll, fields.Everything())

	queue := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())

	informer := cache.NewSharedIndexInformer(
		lw,
		&spec.Function{},
		0,
		cache.Indexers{},
	)

	informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			key, err := cache.MetaNamespaceKeyFunc(obj)
			if err == nil {
				queue.Add(key)
			}
		},
		UpdateFunc: func(old, new interface{}) {
			key, err := cache.MetaNamespaceKeyFunc(new)
			if err == nil {
				queue.Add(key)
			}
		},
		DeleteFunc: func(obj interface{}) {
			key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
			if err == nil {
				queue.Add(key)
			}
		},
	})

	return &Controller{
		logger:    logrus.WithField("pkg", "controller"),
		clientset: cfg.KubeCli,
		tprclient: cfg.TprClient,
		informer:  informer,
		queue:     queue,
	}
}

// Init creates tpr functions.k8s.io
func (c *Controller) Init() {
	c.logger.Infof("Initializing Kubeless controller...")
	for {
		//create TPR if it's not exists
		err := initResource(c.clientset)
		if err == nil {
			break
		}
		c.logger.Errorf("Initialization failed: %v", err)
		c.logger.Infof("Retry in %v...", initRetryWaitTime)
		time.Sleep(initRetryWaitTime)
	}
}

// InstallKubeless deploys kubeless-controller
func (c *Controller) InstallKubeless(ctlNamespace string) {
	c.logger.Infof("Installing Kubeless controller into Kubernetes deployment...")
	err := utils.DeployKubeless(c.clientset, ctlNamespace)
	if err != nil {
		c.logger.Errorf("Kubeless controller installation failed: %v", err)
	} else {
		c.logger.Infof("Kubeless controller installation successful!")
	}
}

// InstallMsgBroker deploys kafka-controller
func (c *Controller) InstallMsgBroker(ctlNamespace string) {
	c.logger.Infof("Installing Message Broker into Kubernetes deployment...")
	err := utils.DeployMsgBroker(c.clientset, ctlNamespace)
	if err != nil {
		c.logger.Errorf("Message Broker installation failed: %v", err)
	} else {
		c.logger.Infof("Message Broker installation successful!")
	}
}

// Run starts the kubeless controller
func (c *Controller) Run(stopCh <-chan struct{}) {
	defer utilruntime.HandleCrash()
	defer c.queue.ShutDown()

	c.logger.Info("Starting kubeless controller")

	go c.informer.Run(stopCh)

	if !cache.WaitForCacheSync(stopCh, c.HasSynced) {
		utilruntime.HandleError(fmt.Errorf("Timed out waiting for caches to sync"))
		return
	}

	c.logger.Info("Kubeless controller synced and ready")

	// run one round of GC at startup to detect orphaned objects from the last time
	c.garbageCollect()

	wait.Until(c.runWorker, time.Second, stopCh)
}

// HasSynced is required for the cache.Controller interface.
func (c *Controller) HasSynced() bool {
	return c.informer.HasSynced()
}

// LastSyncResourceVersion is required for the cache.Controller interface.
func (c *Controller) LastSyncResourceVersion() string {
	return c.informer.LastSyncResourceVersion()
}

func (c *Controller) runWorker() {
	for c.processNextItem() {
		// continue looping
	}
}

func (c *Controller) processNextItem() bool {
	key, quit := c.queue.Get()
	if quit {
		return false
	}
	defer c.queue.Done(key)

	err := c.processItem(key.(string))
	if err == nil {
		// No error, reset the ratelimit counters
		c.queue.Forget(key)
	} else if c.queue.NumRequeues(key) < maxRetries {
		c.logger.Errorf("Error processing %s (will retry): %v", key, err)
		c.queue.AddRateLimited(key)
	} else {
		// err != nil and too many retries
		c.logger.Errorf("Error processing %s (giving up): %v", key, err)
		c.queue.Forget(key)
		utilruntime.HandleError(err)
	}

	return true
}

func (c *Controller) processItem(key string) error {
	c.logger.Infof("Processing change to Function %s", key)

	ns, name, err := cache.SplitMetaNamespaceKey(key)
	if err != nil {
		return err
	}

	obj, exists, err := c.informer.GetIndexer().GetByKey(key)
	if err != nil {
		return fmt.Errorf("Error fetching object with key %s from store: %v", key, err)
	}

	if !exists {
		err := utils.DeleteK8sResources(ns, name, c.clientset)
		if err != nil {
			c.logger.Errorf("Can't delete function: %v", err)
			return err
		}
		c.logger.Infof("Deleted Function %s", key)
		return nil
	}

	funcObj := obj.(*spec.Function)

	err = utils.EnsureK8sResources(ns, name, funcObj, c.clientset)
	if err != nil {
		c.logger.Errorf("Function can not be created/updated: %v", err)
		return err
	}

	c.logger.Infof("Updated Function %s", key)
	return nil
}

func initResource(clientset kubernetes.Interface) error {
	tpr := &v1beta1.ThirdPartyResource{
		ObjectMeta: metav1.ObjectMeta{
			Name: tprName,
		},
		Versions: []v1beta1.APIVersion{
			{Name: "v1"},
		},
		Description: "Kubeless: Serverless framework for Kubernetes",
	}

	_, err := clientset.Extensions().ThirdPartyResources().Create(tpr)
	if err != nil && k8sErrors.IsAlreadyExists(err) {
		_, err = clientset.Extensions().ThirdPartyResources().Update(tpr)
	}
	if err != nil {
		return err
	}

	return nil
}

func (c *Controller) garbageCollect() error {
	functionList := spec.FunctionList{}
	err := c.tprclient.Get().Resource("functions").Do().Into(&functionList)
	if err != nil {
		return err
	}

	functionUIDSet := make(map[types.UID]bool)
	for _, f := range functionList.Items {
		functionUIDSet[f.Metadata.UID] = true
	}

	if err = c.collectServices(functionUIDSet); err != nil {
		return err
	}
	if err = c.collectDeployment(functionUIDSet); err != nil {
		return err
	}
	if err = c.collectConfigMap(functionUIDSet); err != nil {
		return err
	}

	return nil
}

func (c *Controller) collectServices(functionUIDSet map[types.UID]bool) error {
	srvs, err := c.clientset.CoreV1().Services(api.NamespaceAll).List(metav1.ListOptions{})
	if err != nil {
		return err
	}

	for _, srv := range srvs.Items {
		if len(srv.OwnerReferences) == 0 {
			continue
		}
		if !functionUIDSet[srv.OwnerReferences[0].UID] {
			//FIXME: service and its function are deployed in the same namespace
			key := fmt.Sprintf("%s/%s", srv.Namespace, srv.OwnerReferences[0].Name)
			//FIXME: should we check if the key is already exist in queue
			c.queue.Add(key)
		}
	}

	return nil
}

func (c *Controller) collectDeployment(functionUIDSet map[types.UID]bool) error {
	ds, err := c.clientset.AppsV1beta1().Deployments(api.NamespaceAll).List(metav1.ListOptions{})
	if err != nil {
		return err
	}

	for _, d := range ds.Items {
		if len(d.OwnerReferences) == 0 {
			continue
		}
		if !functionUIDSet[d.OwnerReferences[0].UID] {
			key := fmt.Sprintf("%s/%s", d.Namespace, d.OwnerReferences[0].Name)
			c.queue.Add(key)
		}
	}

	return nil
}

func (c *Controller) collectConfigMap(functionUIDSet map[types.UID]bool) error {
	cm, err := c.clientset.CoreV1().ConfigMaps(api.NamespaceAll).List(metav1.ListOptions{})
	if err != nil {
		return err
	}

	for _, m := range cm.Items {
		if len(m.OwnerReferences) == 0 {
			continue
		}
		if !functionUIDSet[m.OwnerReferences[0].UID] {
			key := fmt.Sprintf("%s/%s", m.Namespace, m.OwnerReferences[0].Name)
			c.queue.Add(key)
		}
	}

	return nil
}
<commit_msg>update gc strategy<commit_after>/*
Copyright 2016 Skippbox, Ltd.

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 controller

import (
	"errors"
	"fmt"
	"time"

	"github.com/Sirupsen/logrus"
	k8sErrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/pkg/api"
	"k8s.io/client-go/pkg/apis/extensions/v1beta1"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/util/workqueue"

	"github.com/kubeless/kubeless/pkg/spec"
	"github.com/kubeless/kubeless/pkg/utils"
)

const (
	tprName    = "function.k8s.io"
	maxRetries = 5
	funcKind   = "Function"
	funcAPI    = "k8s.io"
)

var (
	errVersionOutdated = errors.New("Requested version is outdated in apiserver")
	initRetryWaitTime  = 30 * time.Second
)

// Controller object
type Controller struct {
	logger    *logrus.Entry
	clientset kubernetes.Interface
	tprclient rest.Interface
	Functions map[string]*spec.Function
	queue     workqueue.RateLimitingInterface
	informer  cache.SharedIndexInformer
}

// Config contains k8s client of a controller
type Config struct {
	KubeCli   kubernetes.Interface
	TprClient rest.Interface
}

// New initializes a controller object
func New(cfg Config) *Controller {
	lw := cache.NewListWatchFromClient(cfg.TprClient, "functions", api.NamespaceAll, fields.Everything())

	queue := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())

	informer := cache.NewSharedIndexInformer(
		lw,
		&spec.Function{},
		0,
		cache.Indexers{},
	)

	informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			key, err := cache.MetaNamespaceKeyFunc(obj)
			if err == nil {
				queue.Add(key)
			}
		},
		UpdateFunc: func(old, new interface{}) {
			key, err := cache.MetaNamespaceKeyFunc(new)
			if err == nil {
				queue.Add(key)
			}
		},
		DeleteFunc: func(obj interface{}) {
			key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
			if err == nil {
				queue.Add(key)
			}
		},
	})

	return &Controller{
		logger:    logrus.WithField("pkg", "controller"),
		clientset: cfg.KubeCli,
		tprclient: cfg.TprClient,
		informer:  informer,
		queue:     queue,
	}
}

// Init creates tpr functions.k8s.io
func (c *Controller) Init() {
	c.logger.Infof("Initializing Kubeless controller...")
	for {
		//create TPR if it's not exists
		err := initResource(c.clientset)
		if err == nil {
			break
		}
		c.logger.Errorf("Initialization failed: %v", err)
		c.logger.Infof("Retry in %v...", initRetryWaitTime)
		time.Sleep(initRetryWaitTime)
	}
}

// InstallKubeless deploys kubeless-controller
func (c *Controller) InstallKubeless(ctlNamespace string) {
	c.logger.Infof("Installing Kubeless controller into Kubernetes deployment...")
	err := utils.DeployKubeless(c.clientset, ctlNamespace)
	if err != nil {
		c.logger.Errorf("Kubeless controller installation failed: %v", err)
	} else {
		c.logger.Infof("Kubeless controller installation successful!")
	}
}

// InstallMsgBroker deploys kafka-controller
func (c *Controller) InstallMsgBroker(ctlNamespace string) {
	c.logger.Infof("Installing Message Broker into Kubernetes deployment...")
	err := utils.DeployMsgBroker(c.clientset, ctlNamespace)
	if err != nil {
		c.logger.Errorf("Message Broker installation failed: %v", err)
	} else {
		c.logger.Infof("Message Broker installation successful!")
	}
}

// Run starts the kubeless controller
func (c *Controller) Run(stopCh <-chan struct{}) {
	defer utilruntime.HandleCrash()
	defer c.queue.ShutDown()

	c.logger.Info("Starting kubeless controller")

	go c.informer.Run(stopCh)

	if !cache.WaitForCacheSync(stopCh, c.HasSynced) {
		utilruntime.HandleError(fmt.Errorf("Timed out waiting for caches to sync"))
		return
	}

	c.logger.Info("Kubeless controller synced and ready")

	// run one round of GC at startup to detect orphaned objects from the last time
	c.garbageCollect()

	wait.Until(c.runWorker, time.Second, stopCh)
}

// HasSynced is required for the cache.Controller interface.
func (c *Controller) HasSynced() bool {
	return c.informer.HasSynced()
}

// LastSyncResourceVersion is required for the cache.Controller interface.
func (c *Controller) LastSyncResourceVersion() string {
	return c.informer.LastSyncResourceVersion()
}

func (c *Controller) runWorker() {
	for c.processNextItem() {
		// continue looping
	}
}

func (c *Controller) processNextItem() bool {
	key, quit := c.queue.Get()
	if quit {
		return false
	}
	defer c.queue.Done(key)

	err := c.processItem(key.(string))
	if err == nil {
		// No error, reset the ratelimit counters
		c.queue.Forget(key)
	} else if c.queue.NumRequeues(key) < maxRetries {
		c.logger.Errorf("Error processing %s (will retry): %v", key, err)
		c.queue.AddRateLimited(key)
	} else {
		// err != nil and too many retries
		c.logger.Errorf("Error processing %s (giving up): %v", key, err)
		c.queue.Forget(key)
		utilruntime.HandleError(err)
	}

	return true
}

func (c *Controller) processItem(key string) error {
	c.logger.Infof("Processing change to Function %s", key)

	ns, name, err := cache.SplitMetaNamespaceKey(key)
	if err != nil {
		return err
	}

	obj, exists, err := c.informer.GetIndexer().GetByKey(key)
	if err != nil {
		return fmt.Errorf("Error fetching object with key %s from store: %v", key, err)
	}

	if !exists {
		err := utils.DeleteK8sResources(ns, name, c.clientset)
		if err != nil {
			c.logger.Errorf("Can't delete function: %v", err)
			return err
		}
		c.logger.Infof("Deleted Function %s", key)
		return nil
	}

	funcObj := obj.(*spec.Function)

	err = utils.EnsureK8sResources(ns, name, funcObj, c.clientset)
	if err != nil {
		c.logger.Errorf("Function can not be created/updated: %v", err)
		return err
	}

	c.logger.Infof("Updated Function %s", key)
	return nil
}

func initResource(clientset kubernetes.Interface) error {
	tpr := &v1beta1.ThirdPartyResource{
		ObjectMeta: metav1.ObjectMeta{
			Name: tprName,
		},
		Versions: []v1beta1.APIVersion{
			{Name: "v1"},
		},
		Description: "Kubeless: Serverless framework for Kubernetes",
	}

	_, err := clientset.Extensions().ThirdPartyResources().Create(tpr)
	if err != nil && k8sErrors.IsAlreadyExists(err) {
		_, err = clientset.Extensions().ThirdPartyResources().Update(tpr)
	}
	if err != nil {
		return err
	}

	return nil
}

func (c *Controller) garbageCollect() error {
	if err := c.collectServices(); err != nil {
		return err
	}
	if err := c.collectDeployment(); err != nil {
		return err
	}
	if err := c.collectConfigMap(); err != nil {
		return err
	}

	return nil
}

func (c *Controller) collectServices() error {
	srvs, err := c.clientset.CoreV1().Services(api.NamespaceAll).List(metav1.ListOptions{})
	if err != nil {
		return err
	}

	for _, srv := range srvs.Items {
		if len(srv.OwnerReferences) == 0 {
			continue
		}
		// Include the derived key from existing svc owner reference to the workqueue
		// This will make sure the controller can detect the non-existing function and
		// react to delete its belonging objects
		// Assumption: a service has ownerref Kind = "Function" and APIVersion = "k8s.io" is assumed
		// to be created by kubeless controller
		if (srv.OwnerReferences[0].Kind == funcKind) && (srv.OwnerReferences[0].APIVersion == funcAPI) {
			//service and its function are deployed in the same namespace
			key := fmt.Sprintf("%s/%s", srv.Namespace, srv.OwnerReferences[0].Name)
			c.queue.Add(key)
		}
	}

	return nil
}

func (c *Controller) collectDeployment() error {
	ds, err := c.clientset.AppsV1beta1().Deployments(api.NamespaceAll).List(metav1.ListOptions{})
	if err != nil {
		return err
	}

	for _, d := range ds.Items {
		if len(d.OwnerReferences) == 0 {
			continue
		}
		// Assumption: a deployment has ownerref Kind = "Function" and APIVersion = "k8s.io" is assumed
		// to be created by kubeless controller
		if (d.OwnerReferences[0].Kind == funcKind) && (d.OwnerReferences[0].APIVersion == funcAPI) {
			key := fmt.Sprintf("%s/%s", d.Namespace, d.OwnerReferences[0].Name)
			c.queue.Add(key)
		}
	}

	return nil
}

func (c *Controller) collectConfigMap() error {
	cm, err := c.clientset.CoreV1().ConfigMaps(api.NamespaceAll).List(metav1.ListOptions{})
	if err != nil {
		return err
	}

	for _, m := range cm.Items {
		if len(m.OwnerReferences) == 0 {
			continue
		}
		// Assumption: a configmap has ownerref Kind = "Function" and APIVersion = "k8s.io" is assumed
		// to be created by kubeless controller
		if (m.OwnerReferences[0].Kind == funcKind) && (m.OwnerReferences[0].APIVersion == funcAPI) {
			key := fmt.Sprintf("%s/%s", m.Namespace, m.OwnerReferences[0].Name)
			c.queue.Add(key)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package mocks

import "github.com/materials-commons/testify/mock"

import "github.com/materials-commons/mcstore/pkg/db/schema"

type Projects struct {
	mock.Mock
}

func NewMProjects() *Projects {
	return &Projects{}
}

func (m *Projects) ByID(id string) (*schema.Project, error) {
	ret := m.Called(id)

	r0 := ret.Get(0).(*schema.Project)
	r1 := ret.Error(1)

	return r0, r1
}

func (m *Projects) ByName(name, owner string) (*schema.Project, error) {
	ret := m.Called(name, owner)

	r0 := ret.Get(0).(*schema.Project)
	r1 := ret.Error(1)

	return r0, r1
}

func (m *Projects) Insert(project *schema.Project) (*schema.Project, error) {
	ret := m.Called(project)
	r0 := ret.Get(0).(*schema.Project)
	r1 := ret.Error(1)
	return r0, r1
}

func (m *Projects) HasDirectory(projectID, directoryID string) bool {
	ret := m.Called(projectID, directoryID)
	r0 := ret.Get(0).(bool)
	return r0
}

func (m *Projects) AccessList(projectID string) ([]schema.Access, error) {
	ret := m.Called(projectID)
	r0 := ret.Get(0).([]schema.Access)
	r1 := ret.Error(1)
	return r0, r1
}
<commit_msg>Add second type of mock.<commit_after>package mocks

import (
	"fmt"

	"github.com/materials-commons/testify/mock"
)

import "github.com/materials-commons/mcstore/pkg/db/schema"

type Projects struct {
	mock.Mock
}

func NewMProjects() *Projects {
	return &Projects{}
}

func (m *Projects) ByID(id string) (*schema.Project, error) {
	ret := m.Called(id)

	r0 := ret.Get(0).(*schema.Project)
	r1 := ret.Error(1)

	return r0, r1
}

func (m *Projects) ByName(name, owner string) (*schema.Project, error) {
	ret := m.Called(name, owner)

	r0 := ret.Get(0).(*schema.Project)
	r1 := ret.Error(1)

	return r0, r1
}

func (m *Projects) Insert(project *schema.Project) (*schema.Project, error) {
	ret := m.Called(project)
	r0 := ret.Get(0).(*schema.Project)
	r1 := ret.Error(1)
	return r0, r1
}

func (m *Projects) HasDirectory(projectID, directoryID string) bool {
	ret := m.Called(projectID, directoryID)
	r0 := ret.Get(0).(bool)
	return r0
}

func (m *Projects) AccessList(projectID string) ([]schema.Access, error) {
	ret := m.Called(projectID)
	r0 := ret.Get(0).([]schema.Access)
	r1 := ret.Error(1)
	return r0, r1
}

type pentry struct {
	project *schema.Project
	hasDir  bool
	err     error
	access  []schema.Access
}

type Projects2 struct {
	method        map[string]*pentry
	currentMethod string
}

func NewMProjects2() *Projects2 {
	return &Projects2{
		method: make(map[string]*pentry),
	}
}

func (m *Projects2) lookup(method string) *pentry {
	if e, ok := m.method[method]; ok {
		return e
	}
	panic(fmt.Sprintf("Unable to find method: %s", method))
}

func (m *Projects2) ByID(id string) (*schema.Project, error) {
	e := m.lookup("ByID")
	return e.project, e.err
}

func (m *Projects2) ByName(name, owner string) (*schema.Project, error) {
	e := m.lookup("ByName")
	return e.project, e.err
}

func (m *Projects2) Insert(project *schema.Project) (*schema.Project, error) {
	e := m.lookup("Insert")
	return e.project, e.err
}

func (m *Projects2) HasDirectory(projectID, directoryID string) bool {
	e := m.lookup("HasDirectory")
	return e.hasDir
}

func (m *Projects2) AccessList(projectID string) ([]schema.Access, error) {
	e := m.lookup("AccessList")
	return e.access, e.err
}

func (m *Projects2) On(method string) *Projects2 {
	m.currentMethod = method
	m.method[method] = &pentry{}
	return m
}

func (m *Projects2) SetError(err error) *Projects2 {
	m.method[m.currentMethod].err = err
	return m
}

func (m *Projects2) SetProject(project *schema.Project) *Projects2 {
	m.method[m.currentMethod].project = project
	return m
}

func (m *Projects2) SetHasDir(hasDir bool) *Projects2 {
	m.method[m.currentMethod].hasDir = hasDir
	return m
}

func (m *Projects2) SetAccessList(access []schema.Access) *Projects2 {
	m.method[m.currentMethod].access = access
	return m
}
<|endoftext|>


<commit_before>package entity

import (
	"testing"

	"rsprd.com/spread/pkg/deploy"

	"github.com/gh/stretchr/testify/assert"
	"k8s.io/kubernetes/pkg/api"
)

func TestContainerWithImageDeployment(t *testing.T) {
	const imageName = "busybox:latest"
	kubeContainer := api.Container{
		Name:            "simple-container",
		Image:           imageName,
		Command:         []string{"/bin/busybox", "ls"},
		ImagePullPolicy: api.PullAlways,
	}

	ctr, err := NewContainer(kubeContainer, api.ObjectMeta{}, "simpleTest")
	assert.NoError(t, err, "should be able to create container")
	assert.NotNil(t, ctr.image, "an image should have been created")

	// check images
	images := ctr.Images()
	assert.Len(t, images, 1, "should have single image")

	expectedImage := newDockerImage(t, imageName)
	actualImage := images[0]
	assert.Equal(t, expectedImage.DockerName(), actualImage.DockerName(), "image should not have changed")

	// check kube
	kube, err := ctr.kube()
	assert.NoError(t, err, "should be able to produce kube")
	assert.True(t, api.Semantic.DeepEqual(&kube, &kubeContainer), "kube should be same as container")

	pod := api.Pod{
		ObjectMeta: api.ObjectMeta{
			GenerateName: kubeContainer.Name,
			Namespace:    api.NamespaceDefault,
		},
		Spec: api.PodSpec{
			Containers: []api.Container{
				kubeContainer,
			},
		},
	}

	expected := deploy.Deployment{}
	assert.NoError(t, expected.Add(&pod), "valid pod")
	actual, err := ctr.Deployment()
	assert.NoError(t, err, "deploy ok")

	assert.True(t, expected.Equal(actual), "should be equivlant")
}

func TestContainerNoImageDeployment(t *testing.T) {
	kubeContainer := api.Container{
		Name: "no-image-container",
		// no image
		Command:         []string{"/bin/busybox", "ls"},
		ImagePullPolicy: api.PullAlways,
	}

	ctr, err := NewContainer(kubeContainer, api.ObjectMeta{}, "noImage")
	assert.NoError(t, err, "should be able to create container")
	assert.Nil(t, ctr.image, "no image should exist")

	images := ctr.Images()
	assert.Len(t, images, 0, "no image should have been created")

	_, err = ctr.kube()
	assert.Error(t, err, "container is not ready")

	_, err = ctr.Deployment()
	assert.Error(t, err, "cannot be deployed without image")
}

func TestContainerAttach(t *testing.T) {
	imageName := "to-be-attached"
	image := testNewImage(t, imageName, api.ObjectMeta{}, "test", []deploy.KubeObject{})

	kubeContainer := newKubeContainer("test-container", "") // no image
	container, err := NewContainer(kubeContainer, api.ObjectMeta{}, "attach")
	assert.NoError(t, err, "valid container")

	_, err = container.Deployment()
	assert.Error(t, err, "cannot be deployed without image")

	err = container.Attach(image)
	assert.NoError(t, err, "attach should be allowed")

	kubeContainer.Image = imageName
	pod := api.Pod{
		ObjectMeta: api.ObjectMeta{
			GenerateName: kubeContainer.Name,
			Namespace:    api.NamespaceDefault,
		},
		Spec: api.PodSpec{
			Containers: []api.Container{
				kubeContainer,
			},
		},
	}

	expected := deploy.Deployment{}
	assert.NoError(t, expected.Add(&pod), "valid pod")
	actual, err := container.Deployment()
	assert.NoError(t, err, "deploy ok")

	assert.True(t, expected.Equal(actual), "should be equivlant")
}

func newKubeContainer(name, imageName string) api.Container {
	return api.Container{
		Name:            name,
		Image:           imageName,
		Command:         []string{"/bin/busybox", "ls"},
		ImagePullPolicy: api.PullAlways,
	}
}
<commit_msg>added container tests<commit_after>package entity

import (
	"testing"

	"rsprd.com/spread/pkg/deploy"

	"github.com/gh/stretchr/testify/assert"
	"k8s.io/kubernetes/pkg/api"
)

func TestContainerWithImageDeployment(t *testing.T) {
	const imageName = "busybox:latest"
	kubeContainer := api.Container{
		Name:            "simple-container",
		Image:           imageName,
		Command:         []string{"/bin/busybox", "ls"},
		ImagePullPolicy: api.PullAlways,
	}

	ctr, err := NewContainer(kubeContainer, api.ObjectMeta{}, "simpleTest")
	assert.NoError(t, err, "should be able to create container")
	assert.NotNil(t, ctr.image, "an image should have been created")

	// check images
	images := ctr.Images()
	assert.Len(t, images, 1, "should have single image")

	expectedImage := newDockerImage(t, imageName)
	actualImage := images[0]
	assert.Equal(t, expectedImage.DockerName(), actualImage.DockerName(), "image should not have changed")

	// check kube
	kube, err := ctr.kube()
	assert.NoError(t, err, "should be able to produce kube")
	assert.True(t, api.Semantic.DeepEqual(&kube, &kubeContainer), "kube should be same as container")

	pod := api.Pod{
		ObjectMeta: api.ObjectMeta{
			GenerateName: kubeContainer.Name,
			Namespace:    api.NamespaceDefault,
		},
		Spec: api.PodSpec{
			Containers: []api.Container{
				kubeContainer,
			},
		},
	}

	expected := deploy.Deployment{}
	assert.NoError(t, expected.Add(&pod), "valid pod")
	actual, err := ctr.Deployment()
	assert.NoError(t, err, "deploy ok")

	assert.True(t, expected.Equal(actual), "should be equivlant")
}

func TestContainerNoImageDeployment(t *testing.T) {
	kubeContainer := api.Container{
		Name: "no-image-container",
		// no image
		Command:         []string{"/bin/busybox", "ls"},
		ImagePullPolicy: api.PullAlways,
	}

	ctr, err := NewContainer(kubeContainer, api.ObjectMeta{}, "noImage")
	assert.NoError(t, err, "should be able to create container")
	assert.Nil(t, ctr.image, "no image should exist")

	images := ctr.Images()
	assert.Len(t, images, 0, "no image should have been created")

	_, err = ctr.kube()
	assert.Error(t, err, "container is not ready")

	_, err = ctr.Deployment()
	assert.Error(t, err, "cannot be deployed without image")
}

func TestContainerAttach(t *testing.T) {
	imageName := "to-be-attached"
	image := testNewImage(t, imageName, api.ObjectMeta{}, "test", []deploy.KubeObject{})

	kubeContainer := newKubeContainer("test-container", "") // no image
	container, err := NewContainer(kubeContainer, api.ObjectMeta{}, "attach")
	assert.NoError(t, err, "valid container")

	_, err = container.Deployment()
	assert.Error(t, err, "cannot be deployed without image")

	err = container.Attach(image)
	assert.NoError(t, err, "attach should be allowed")

	kubeContainer.Image = imageName
	pod := api.Pod{
		ObjectMeta: api.ObjectMeta{
			GenerateName: kubeContainer.Name,
			Namespace:    api.NamespaceDefault,
		},
		Spec: api.PodSpec{
			Containers: []api.Container{
				kubeContainer,
			},
		},
	}

	expected := deploy.Deployment{}
	assert.NoError(t, expected.Add(&pod), "valid pod")
	actual, err := container.Deployment()
	assert.NoError(t, err, "deploy ok")

	assert.True(t, expected.Equal(actual), "should be equivlant")
}

func TestContainerBadObject(t *testing.T) {
	kubeContainer := newKubeContainer("test-container", "test-image")
	objects := []deploy.KubeObject{
		createSecret(""), // invalid - must have name
	}

	_, err := NewContainer(kubeContainer, api.ObjectMeta{}, "invalidobjects", objects...)
	assert.Error(t, err, "container should not be created with invalid objects")
}

func TestContainerInvalidContainer(t *testing.T) {
	kubeContainer := api.Container{
		// invalid - no name
		Image:           "invalid-container",
		Command:         []string{"/bin/busybox", "ls"},
		ImagePullPolicy: api.PullAlways,
	}
	_, err := NewContainer(kubeContainer, api.ObjectMeta{}, "invalidcontainer")
	assert.Error(t, err, "name is missing, container is invalid")
}

func TestContainerInvalidImage(t *testing.T) {
	imageName := "*T*H*I*S* IS ILLEGAL"
	kubeContainer := newKubeContainer("invalid-image", imageName)
	_, err := NewContainer(kubeContainer, api.ObjectMeta{}, "invalidimage")
	assert.Error(t, err, "image was invalid")
}

func newKubeContainer(name, imageName string) api.Container {
	return api.Container{
		Name:            name,
		Image:           imageName,
		Command:         []string{"/bin/busybox", "ls"},
		ImagePullPolicy: api.PullAlways,
	}
}
<|endoftext|>


<commit_before>package kmodule

import (
	"bufio"
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"
	"syscall"
	"unsafe"

	"golang.org/x/sys/unix"
)

// Flags to finit_module(2) / FileInit.
const (
	// Ignore symbol version hashes.
	MODULE_INIT_IGNORE_MODVERSIONS = 0x1

	// Ignore kernel version magic.
	MODULE_INIT_IGNORE_VERMAGIC = 0x2
)

// SyscallError contains an error message as well as the actual syscall Errno
type SyscallError struct {
	Msg   string
	Errno syscall.Errno
}

func (s *SyscallError) Error() string {
	if s.Errno != 0 {
		return fmt.Sprintf("%s: %v", s.Msg, s.Errno)
	}
	return s.Msg
}

// Init loads the kernel module given by image with the given options.
func Init(image []byte, opts string) error {
	optsNull, err := unix.BytePtrFromString(opts)
	if err != nil {
		return &SyscallError{Msg: fmt.Sprintf("kmodule.Init: could not convert %q to C string: %v", opts, err)}
	}

	if _, _, e := unix.Syscall(unix.SYS_INIT_MODULE, uintptr(unsafe.Pointer(&image[0])), uintptr(len(image)), uintptr(unsafe.Pointer(optsNull))); e != 0 {
		return &SyscallError{
			Msg:   fmt.Sprintf("init_module(%v, %q) failed", image, opts),
			Errno: e,
		}
	}

	return nil
}

// FileInit loads the kernel module contained by `f` with the given opts and
// flags.
//
// FileInit falls back to Init when the finit_module(2) syscall is not available.
func FileInit(f *os.File, opts string, flags uintptr) error {
	optsNull, err := unix.BytePtrFromString(opts)
	if err != nil {
		return &SyscallError{Msg: fmt.Sprintf("kmodule.Init: could not convert %q to C string: %v", opts, err)}
	}

	if _, _, e := unix.Syscall(unix.SYS_FINIT_MODULE, f.Fd(), uintptr(unsafe.Pointer(optsNull)), flags); e == unix.ENOSYS {
		if flags != 0 {
			return &SyscallError{Msg: fmt.Sprintf("finit_module unavailable"), Errno: e}
		}

		// Fall back to regular init_module(2).
		img, err := ioutil.ReadAll(f)
		if err != nil {
			return &SyscallError{Msg: fmt.Sprintf("kmodule.FileInit: %v", err)}
		}
		return Init(img, opts)
	} else if e != 0 {
		return &SyscallError{
			Msg:   fmt.Sprintf("finit_module(%v, %q, %#x) failed", f, opts, flags),
			Errno: e,
		}
	}

	return nil
}

// Delete removes a kernel module.
func Delete(name string, flags uintptr) error {
	modnameptr, err := unix.BytePtrFromString(name)
	if err != nil {
		return &SyscallError{Msg: fmt.Sprintf("could not delete module %q: %v", name, err)}
	}

	if _, _, e := unix.Syscall(unix.SYS_DELETE_MODULE, uintptr(unsafe.Pointer(modnameptr)), flags, 0); e != 0 {
		return &SyscallError{Msg: fmt.Sprintf("could not delete module %q", name), Errno: e}
	}

	return nil
}

type modState uint8

const (
	unloaded modState = iota
	loading
	loaded
)

type dependency struct {
	state modState
	deps  []string
}

type depMap map[string]*dependency

// ProbeOpts contains optional parameters to Probe.
//
// An empty ProbeOpts{} should lead to the default behavior.
type ProbeOpts struct {
	DryRun bool
}

// Probe loads the given kernel module and its dependencies.
// It is calls ProbeOptions with the default ProbeOpts.
func Probe(name string, modParams string) error {
	return ProbeOptions(name, modParams, ProbeOpts{})
}

// ProbeOptions loads the given kernel module and its dependencies.
// This functions takes ProbeOpts.
func ProbeOptions(name, modParams string, opts ProbeOpts) error {
	deps, err := genDeps()
	if err != nil {
		return &SyscallError{Msg: fmt.Sprintf("could not generate dependency map %v", err)}
	}

	modPath, err := findModPath(name, deps)
	if err != nil {
		return &SyscallError{Msg: fmt.Sprintf("could not find module path %q: %v", name, err)}
	}

	if !opts.DryRun {
		// if the module is already loaded or does not have deps, or all of them are loaded
		// then this succeeds and we are done
		if err := loadModule(modPath, modParams, opts); err == nil {
			return nil
		}
		// okay, we have to try the hard way and load dependencies first.
	} else {
		fmt.Println("Unique dependencies in load order, already loaded ones get skipped:")
	}

	deps[modPath].state = loading
	for _, d := range deps[modPath].deps {
		if err := loadDeps(d, deps, opts); err != nil {
			return err
		}
	}
	if err := loadModule(modPath, modParams, opts); err != nil {
		return err
	}
	// we don't care to set the state to loaded
	// deps[modPath].state = loaded
	return nil
}

func genDeps() (depMap, error) {
	deps := make(depMap)

	var u unix.Utsname
	if err := unix.Uname(&u); err != nil {
		return nil, fmt.Errorf("could not get release (uname -r): %v", err)
	}
	rel := string(u.Release[:bytes.IndexByte(u.Release[:], 0)])

	moduleDir := filepath.Join("/lib/modules", strings.TrimSpace(rel))

	f, err := os.Open(filepath.Join(moduleDir, "modules.dep"))
	if err != nil {
		return nil, fmt.Errorf("could not open dependency file: %v", err)
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		txt := scanner.Text()
		nameDeps := strings.Split(txt, ":")
		modPath, modDeps := nameDeps[0], nameDeps[1]
		modPath = filepath.Join(moduleDir, strings.TrimSpace(modPath))

		var dependency dependency
		if len(modDeps) > 0 {
			for _, dep := range strings.Split(strings.TrimSpace(modDeps), " ") {
				dependency.deps = append(dependency.deps, filepath.Join(moduleDir, dep))
			}
		}
		deps[modPath] = &dependency
	}

	if err := scanner.Err(); err != nil {
		return nil, err
	}

	return deps, nil
}

func findModPath(name string, m depMap) (string, error) {
	for mp := range m {
		if path.Base(mp) == name+".ko" {
			return mp, nil
		}
	}

	return "", fmt.Errorf("Could not find path for module %q", name)
}

func loadDeps(path string, m depMap, opts ProbeOpts) error {
	dependency, ok := m[path]
	if !ok {
		return &SyscallError{Msg: fmt.Sprintf("could not find dependency %q", path)}
	}

	if dependency.state == loading {
		return &SyscallError{Msg: fmt.Sprintf("circular dependency! %q already LOADING", path)}
	} else if dependency.state == loaded {
		return nil
	}

	m[path].state = loading

	for _, dep := range dependency.deps {
		if err := loadDeps(dep, m, opts); err != nil {
			return err
		}
	}

	// done with dependencies, load module
	if err := loadModule(path, "", opts); err != nil {
		return err
	}
	m[path].state = loaded

	return nil
}

func loadModule(path, modParams string, opts ProbeOpts) error {
	if opts.DryRun {
		fmt.Println(path)
		return nil
	}

	f, err := os.Open(path)
	if err != nil {
		return &SyscallError{Msg: fmt.Sprintf("could not open %q: %v", path, err)}
	}
	defer f.Close()

	if err := FileInit(f, modParams, 0); err != nil {
		if serr, ok := err.(*SyscallError); !ok || (ok && serr.Errno != unix.EEXIST) {
			return err
		}
	}

	return nil
}
<commit_msg>Check in both /lib/modules and /usr/lib/modules when loading a kernel module<commit_after>package kmodule

import (
	"bufio"
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"
	"syscall"
	"unsafe"

	"golang.org/x/sys/unix"
)

// Flags to finit_module(2) / FileInit.
const (
	// Ignore symbol version hashes.
	MODULE_INIT_IGNORE_MODVERSIONS = 0x1

	// Ignore kernel version magic.
	MODULE_INIT_IGNORE_VERMAGIC = 0x2

)

// SyscallError contains an error message as well as the actual syscall Errno
type SyscallError struct {
	Msg   string
	Errno syscall.Errno
}

func (s *SyscallError) Error() string {
	if s.Errno != 0 {
		return fmt.Sprintf("%s: %v", s.Msg, s.Errno)
	}
	return s.Msg
}

// Init loads the kernel module given by image with the given options.
func Init(image []byte, opts string) error {
	optsNull, err := unix.BytePtrFromString(opts)
	if err != nil {
		return &SyscallError{Msg: fmt.Sprintf("kmodule.Init: could not convert %q to C string: %v", opts, err)}
	}

	if _, _, e := unix.Syscall(unix.SYS_INIT_MODULE, uintptr(unsafe.Pointer(&image[0])), uintptr(len(image)), uintptr(unsafe.Pointer(optsNull))); e != 0 {
		return &SyscallError{
			Msg:   fmt.Sprintf("init_module(%v, %q) failed", image, opts),
			Errno: e,
		}
	}

	return nil
}

// FileInit loads the kernel module contained by `f` with the given opts and
// flags.
//
// FileInit falls back to Init when the finit_module(2) syscall is not available.
func FileInit(f *os.File, opts string, flags uintptr) error {
	optsNull, err := unix.BytePtrFromString(opts)
	if err != nil {
		return &SyscallError{Msg: fmt.Sprintf("kmodule.Init: could not convert %q to C string: %v", opts, err)}
	}

	if _, _, e := unix.Syscall(unix.SYS_FINIT_MODULE, f.Fd(), uintptr(unsafe.Pointer(optsNull)), flags); e == unix.ENOSYS {
		if flags != 0 {
			return &SyscallError{Msg: fmt.Sprintf("finit_module unavailable"), Errno: e}
		}

		// Fall back to regular init_module(2).
		img, err := ioutil.ReadAll(f)
		if err != nil {
			return &SyscallError{Msg: fmt.Sprintf("kmodule.FileInit: %v", err)}
		}
		return Init(img, opts)
	} else if e != 0 {
		return &SyscallError{
			Msg:   fmt.Sprintf("finit_module(%v, %q, %#x) failed", f, opts, flags),
			Errno: e,
		}
	}

	return nil
}

// Delete removes a kernel module.
func Delete(name string, flags uintptr) error {
	modnameptr, err := unix.BytePtrFromString(name)
	if err != nil {
		return &SyscallError{Msg: fmt.Sprintf("could not delete module %q: %v", name, err)}
	}

	if _, _, e := unix.Syscall(unix.SYS_DELETE_MODULE, uintptr(unsafe.Pointer(modnameptr)), flags, 0); e != 0 {
		return &SyscallError{Msg: fmt.Sprintf("could not delete module %q", name), Errno: e}
	}

	return nil
}

type modState uint8

const (
	unloaded modState = iota
	loading
	loaded
)

type dependency struct {
	state modState
	deps  []string
}

type depMap map[string]*dependency

// ProbeOpts contains optional parameters to Probe.
//
// An empty ProbeOpts{} should lead to the default behavior.
type ProbeOpts struct {
	DryRun bool
}

// Probe loads the given kernel module and its dependencies.
// It is calls ProbeOptions with the default ProbeOpts.
func Probe(name string, modParams string) error {
	return ProbeOptions(name, modParams, ProbeOpts{})
}

// ProbeOptions loads the given kernel module and its dependencies.
// This functions takes ProbeOpts.
func ProbeOptions(name, modParams string, opts ProbeOpts) error {
	deps, err := genDeps()
	if err != nil {
		return &SyscallError{Msg: fmt.Sprintf("could not generate dependency map %v", err)}
	}

	modPath, err := findModPath(name, deps)
	if err != nil {
		return &SyscallError{Msg: fmt.Sprintf("could not find module path %q: %v", name, err)}
	}

	if !opts.DryRun {
		// if the module is already loaded or does not have deps, or all of them are loaded
		// then this succeeds and we are done
		if err := loadModule(modPath, modParams, opts); err == nil {
			return nil
		}
		// okay, we have to try the hard way and load dependencies first.
	} else {
		fmt.Println("Unique dependencies in load order, already loaded ones get skipped:")
	}

	deps[modPath].state = loading
	for _, d := range deps[modPath].deps {
		if err := loadDeps(d, deps, opts); err != nil {
			return err
		}
	}
	if err := loadModule(modPath, modParams, opts); err != nil {
		return err
	}
	// we don't care to set the state to loaded
	// deps[modPath].state = loaded
	return nil
}

func genDeps() (depMap, error) {
	deps := make(depMap)

	var u unix.Utsname
	if err := unix.Uname(&u); err != nil {
		return nil, fmt.Errorf("could not get release (uname -r): %v", err)
	}
	rel := string(u.Release[:bytes.IndexByte(u.Release[:], 0)])

	moduleDirs := []string{"/lib/modules", "/usr/lib/modules"}

	var moduleDir string
	for _, moduleDirs := range(moduleDirs) {
		moduleDir = filepath.Join(moduleDirs, strings.TrimSpace(rel))
		if _, err := os.Stat(moduleDir); err == nil {
			break
		}
	}

	f, err := os.Open(filepath.Join(moduleDir, "modules.dep"))
	if err != nil {
		return nil, fmt.Errorf("could not open dependency file: %v", err)
	}
	defer f.Close()

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		txt := scanner.Text()
		nameDeps := strings.Split(txt, ":")
		modPath, modDeps := nameDeps[0], nameDeps[1]
		modPath = filepath.Join(moduleDir, strings.TrimSpace(modPath))

		var dependency dependency
		if len(modDeps) > 0 {
			for _, dep := range strings.Split(strings.TrimSpace(modDeps), " ") {
				dependency.deps = append(dependency.deps, filepath.Join(moduleDir, dep))
			}
		}
		deps[modPath] = &dependency
	}

	if err := scanner.Err(); err != nil {
		return nil, err
	}

	return deps, nil
}

func findModPath(name string, m depMap) (string, error) {
	for mp := range m {
		if path.Base(mp) == name+".ko" {
			return mp, nil
		}
	}

	return "", fmt.Errorf("Could not find path for module %q", name)
}

func loadDeps(path string, m depMap, opts ProbeOpts) error {
	dependency, ok := m[path]
	if !ok {
		return &SyscallError{Msg: fmt.Sprintf("could not find dependency %q", path)}
	}

	if dependency.state == loading {
		return &SyscallError{Msg: fmt.Sprintf("circular dependency! %q already LOADING", path)}
	} else if dependency.state == loaded {
		return nil
	}

	m[path].state = loading

	for _, dep := range dependency.deps {
		if err := loadDeps(dep, m, opts); err != nil {
			return err
		}
	}

	// done with dependencies, load module
	if err := loadModule(path, "", opts); err != nil {
		return err
	}
	m[path].state = loaded

	return nil
}

func loadModule(path, modParams string, opts ProbeOpts) error {
	if opts.DryRun {
		fmt.Println(path)
		return nil
	}

	f, err := os.Open(path)
	if err != nil {
		return &SyscallError{Msg: fmt.Sprintf("could not open %q: %v", path, err)}
	}
	defer f.Close()

	if err := FileInit(f, modParams, 0); err != nil {
		if serr, ok := err.(*SyscallError); !ok || (ok && serr.Errno != unix.EEXIST) {
			return err
		}
	}

	return nil
}

<|endoftext|>