<commit_before>/*
Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package auth

import (
	"fmt"
	"strings"
	"time"

	. "github.com/onsi/ginkgo"

	auditregv1alpha1 "k8s.io/api/auditregistration/v1alpha1"
	apiv1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/apimachinery/pkg/util/wait"
	auditinternal "k8s.io/apiserver/pkg/apis/audit"
	auditv1 "k8s.io/apiserver/pkg/apis/audit/v1"
	clientset "k8s.io/client-go/kubernetes"
	restclient "k8s.io/client-go/rest"
	"k8s.io/kubernetes/test/e2e/framework"
	"k8s.io/kubernetes/test/utils"
	imageutils "k8s.io/kubernetes/test/utils/image"
)

var _ = SIGDescribe("[Feature:DynamicAudit]", func() {
	f := framework.NewDefaultFramework("audit")
	BeforeEach(func() {
		framework.SkipUnlessProviderIs("gce")
	})

	// TODO: Get rid of [DisabledForLargeClusters] when feature request #53455 is ready.
	It("should dynamically audit API calls [DisabledForLargeClusters]", func() {
		namespace := f.Namespace.Name

		By("Creating a kubernetes client that impersonates an unauthorized anonymous user")
		config, err := framework.LoadConfig()
		framework.ExpectNoError(err, "failed to fetch config")

		config.Impersonate = restclient.ImpersonationConfig{
			UserName: "system:anonymous",
			Groups:   []string{"system:unauthenticated"},
		}
		anonymousClient, err := clientset.NewForConfig(config)
		framework.ExpectNoError(err, "failed to create the anonymous client")

		_, err = f.ClientSet.CoreV1().Namespaces().Create(&apiv1.Namespace{
			ObjectMeta: metav1.ObjectMeta{
				Name: "audit",
			},
		})
		framework.ExpectNoError(err, "failed to create namespace")

		_, err = f.ClientSet.CoreV1().Pods(namespace).Create(&apiv1.Pod{
			ObjectMeta: metav1.ObjectMeta{
				Name: "audit-proxy",
				Labels: map[string]string{
					"app": "audit",
				},
			},
			Spec: apiv1.PodSpec{
				Containers: []apiv1.Container{
					{
						Name:  "proxy",
						Image: imageutils.GetE2EImage(imageutils.AuditProxy),
						Ports: []apiv1.ContainerPort{
							{
								ContainerPort: 8080,
							},
						},
					},
				},
			},
		})
		framework.ExpectNoError(err, "failed to create proxy pod")

		_, err = f.ClientSet.CoreV1().Services(namespace).Create(&apiv1.Service{
			ObjectMeta: metav1.ObjectMeta{
				Name: "audit",
			},
			Spec: apiv1.ServiceSpec{
				Ports: []apiv1.ServicePort{
					{
						Port:       80,
						TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 8080},
					},
				},
				Selector: map[string]string{
					"app": "audit",
				},
			},
		})
		framework.ExpectNoError(err, "failed to create proxy service")

		config, err = framework.LoadConfig()
		framework.ExpectNoError(err, "failed to load config")

		var podIP string
		// get pod ip
		err = wait.Poll(100*time.Millisecond, 10*time.Second, func() (done bool, err error) {
			p, err := f.ClientSet.CoreV1().Pods(namespace).Get("audit-proxy", metav1.GetOptions{})
			if errors.IsNotFound(err) {
				framework.Logf("waiting for audit-proxy pod to be present")
				return false, nil
			} else if err != nil {
				return false, err
			}
			podIP = p.Status.PodIP
			if podIP == "" {
				framework.Logf("waiting for audit-proxy pod IP to be ready")
				return false, nil
			}
			return true, nil
		})
		framework.ExpectNoError(err, "timed out waiting for audit-proxy pod to be ready")

		podURL := fmt.Sprintf("http://%s:8080", podIP)
		// create audit sink
		sink := auditregv1alpha1.AuditSink{
			ObjectMeta: metav1.ObjectMeta{
				Name: "test",
			},
			Spec: auditregv1alpha1.AuditSinkSpec{
				Policy: auditregv1alpha1.Policy{
					Level: auditregv1alpha1.LevelRequestResponse,
					Stages: []auditregv1alpha1.Stage{
						auditregv1alpha1.StageRequestReceived,
						auditregv1alpha1.StageResponseStarted,
						auditregv1alpha1.StageResponseComplete,
						auditregv1alpha1.StagePanic,
					},
				},
				Webhook: auditregv1alpha1.Webhook{
					ClientConfig: auditregv1alpha1.WebhookClientConfig{
						URL: &podURL,
					},
				},
			},
		}

		_, err = f.ClientSet.AuditregistrationV1alpha1().AuditSinks().Create(&sink)
		framework.ExpectNoError(err, "failed to create audit sink")
		framework.Logf("created audit sink")

		// check that we are receiving logs in the proxy
		err = wait.Poll(100*time.Millisecond, 10*time.Second, func() (done bool, err error) {
			logs, err := framework.GetPodLogs(f.ClientSet, namespace, "audit-proxy", "proxy")
			if err != nil {
				framework.Logf("waiting for audit-proxy pod logs to be available")
				return false, nil
			}
			if logs == "" {
				framework.Logf("waiting for audit-proxy pod logs to be non-empty")
				return false, nil
			}
			return true, nil
		})
		framework.ExpectNoError(err, "failed to get logs from audit-proxy pod")

		auditTestUser = "kubernetes-admin"
		testCases := []struct {
			action func()
			events []utils.AuditEvent
		}{
			// Create, get, update, patch, delete, list, watch pods.
			// TODO: dedup this with the main audit test once policy functionality is available
			// https://github.com/kubernetes/kubernetes/issues/70818
			{
				func() {
					pod := &apiv1.Pod{
						ObjectMeta: metav1.ObjectMeta{
							Name: "audit-pod",
						},
						Spec: apiv1.PodSpec{
							Containers: []apiv1.Container{{
								Name:  "pause",
								Image: imageutils.GetPauseImageName(),
							}},
						},
					}
					updatePod := func(pod *apiv1.Pod) {}

					f.PodClient().CreateSync(pod)

					_, err := f.PodClient().Get(pod.Name, metav1.GetOptions{})
					framework.ExpectNoError(err, "failed to get audit-pod")

					podChan, err := f.PodClient().Watch(watchOptions)
					framework.ExpectNoError(err, "failed to create watch for pods")
					for range podChan.ResultChan() {
					}

					f.PodClient().Update(pod.Name, updatePod)

					_, err = f.PodClient().List(metav1.ListOptions{})
					framework.ExpectNoError(err, "failed to list pods")

					_, err = f.PodClient().Patch(pod.Name, types.JSONPatchType, patch)
					framework.ExpectNoError(err, "failed to patch pod")

					f.PodClient().DeleteSync(pod.Name, &metav1.DeleteOptions{}, framework.DefaultPodDeletionTimeout)
				},
				[]utils.AuditEvent{
					{
						Level:             auditinternal.LevelRequestResponse,
						Stage:             auditinternal.StageResponseComplete,
						RequestURI:        fmt.Sprintf("/api/v1/namespaces/%s/pods", namespace),
						Verb:              "create",
						Code:              201,
						User:              auditTestUser,
						Resource:          "pods",
						Namespace:         namespace,
						RequestObject:     true,
						ResponseObject:    true,
						AuthorizeDecision: "allow",
					}, {
						Level:             auditinternal.LevelRequestResponse,
						Stage:             auditinternal.StageResponseComplete,
						RequestURI:        fmt.Sprintf("/api/v1/namespaces/%s/pods/audit-pod", namespace),
						Verb:              "get",
						Code:              200,
						User:              auditTestUser,
						Resource:          "pods",
						Namespace:         namespace,
						RequestObject:     false,
						ResponseObject:    true,
						AuthorizeDecision: "allow",
					}, {
						Level:             auditinternal.LevelRequestResponse,
						Stage:             auditinternal.StageResponseComplete,
						RequestURI:        fmt.Sprintf("/api/v1/namespaces/%s/pods", namespace),
						Verb:              "list",
						Code:              200,
						User:              auditTestUser,
						Resource:          "pods",
						Namespace:         namespace,
						RequestObject:     false,
						ResponseObject:    true,
						AuthorizeDecision: "allow",
					}, {
						Level:             auditinternal.LevelRequestResponse,
						Stage:             auditinternal.StageResponseStarted,
						RequestURI:        fmt.Sprintf("/api/v1/namespaces/%s/pods?timeout=%ds&timeoutSeconds=%d&watch=true", namespace, watchTestTimeout, watchTestTimeout),
						Verb:              "watch",
						Code:              200,
						User:              auditTestUser,
						Resource:          "pods",
						Namespace:         namespace,
						RequestObject:     false,
						ResponseObject:    false,
						AuthorizeDecision: "allow",
					}, {
						Level:             auditinternal.LevelRequestResponse,
						Stage:             auditinternal.StageResponseComplete,
						RequestURI:        fmt.Sprintf("/api/v1/namespaces/%s/pods?timeout=%ds&timeoutSeconds=%d&watch=true", namespace, watchTestTimeout, watchTestTimeout),
						Verb:              "watch",
						Code:              200,
						User:              auditTestUser,
						Resource:          "pods",
						Namespace:         namespace,
						RequestObject:     false,
						ResponseObject:    false,
						AuthorizeDecision: "allow",
					}, {
						Level:             auditinternal.LevelRequestResponse,
						Stage:             auditinternal.StageResponseComplete,
						RequestURI:        fmt.Sprintf("/api/v1/namespaces/%s/pods/audit-pod", namespace),
						Verb:              "update",
						Code:              200,
						User:              auditTestUser,
						Resource:          "pods",
						Namespace:         namespace,
						RequestObject:     true,
						ResponseObject:    true,
						AuthorizeDecision: "allow",
					}, {
						Level:             auditinternal.LevelRequestResponse,
						Stage:             auditinternal.StageResponseComplete,
						RequestURI:        fmt.Sprintf("/api/v1/namespaces/%s/pods/audit-pod", namespace),
						Verb:              "patch",
						Code:              200,
						User:              auditTestUser,
						Resource:          "pods",
						Namespace:         namespace,
						RequestObject:     true,
						ResponseObject:    true,
						AuthorizeDecision: "allow",
					}, {
						Level:             auditinternal.LevelRequestResponse,
						Stage:             auditinternal.StageResponseComplete,
						RequestURI:        fmt.Sprintf("/api/v1/namespaces/%s/pods/audit-pod", namespace),
						Verb:              "delete",
						Code:              200,
						User:              auditTestUser,
						Resource:          "pods",
						Namespace:         namespace,
						RequestObject:     true,
						ResponseObject:    true,
						AuthorizeDecision: "allow",
					},
				},
			},
		}

		// test authorizer annotations, RBAC is required.
		annotationTestCases := []struct {
			action func()
			events []utils.AuditEvent
		}{

			// get a pod with unauthorized user
			{
				func() {
					_, err := anonymousClient.CoreV1().Pods(namespace).Get("another-audit-pod", metav1.GetOptions{})
					expectForbidden(err)
				},
				[]utils.AuditEvent{
					{
						Level:              auditinternal.LevelRequestResponse,
						Stage:              auditinternal.StageResponseComplete,
						RequestURI:         fmt.Sprintf("/api/v1/namespaces/%s/pods/another-audit-pod", namespace),
						Verb:               "get",
						Code:               403,
						User:               auditTestUser,
						ImpersonatedUser:   "system:anonymous",
						ImpersonatedGroups: "system:unauthenticated",
						Resource:           "pods",
						Namespace:          namespace,
						RequestObject:      false,
						ResponseObject:     true,
						AuthorizeDecision:  "forbid",
					},
				},
			},
		}

		if framework.IsRBACEnabled(f) {
			testCases = append(testCases, annotationTestCases...)
		}
		expectedEvents := []utils.AuditEvent{}
		for _, t := range testCases {
			t.action()
			expectedEvents = append(expectedEvents, t.events...)
		}

		// The default flush timeout is 30 seconds, therefore it should be enough to retry once
		// to find all expected events. However, we're waiting for 5 minutes to avoid flakes.
		pollingInterval := 30 * time.Second
		pollingTimeout := 5 * time.Minute
		err = wait.Poll(pollingInterval, pollingTimeout, func() (bool, error) {
			// Fetch the logs
			logs, err := framework.GetPodLogs(f.ClientSet, namespace, "audit-proxy", "proxy")
			if err != nil {
				return false, err
			}
			reader := strings.NewReader(logs)
			missing, err := utils.CheckAuditLines(reader, expectedEvents, auditv1.SchemeGroupVersion)
			if err != nil {
				framework.Logf("Failed to observe audit events: %v", err)
			} else if len(missing) > 0 {
				framework.Logf("Events %#v not found!", missing)
			}
			return len(missing) == 0, nil
		})
		framework.ExpectNoError(err, "after %v failed to observe audit events", pollingTimeout)
		err = f.ClientSet.AuditregistrationV1alpha1().AuditSinks().Delete("test", &metav1.DeleteOptions{})
		framework.ExpectNoError(err, "could not delete audit configuration")
	})
})
<commit_msg>adds owner of TODO comments in dynamic audit e2e test<commit_after>/*
Copyright 2019 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package auth

import (
	"fmt"
	"strings"
	"time"

	. "github.com/onsi/ginkgo"

	auditregv1alpha1 "k8s.io/api/auditregistration/v1alpha1"
	apiv1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/apimachinery/pkg/util/wait"
	auditinternal "k8s.io/apiserver/pkg/apis/audit"
	auditv1 "k8s.io/apiserver/pkg/apis/audit/v1"
	clientset "k8s.io/client-go/kubernetes"
	restclient "k8s.io/client-go/rest"
	"k8s.io/kubernetes/test/e2e/framework"
	"k8s.io/kubernetes/test/utils"
	imageutils "k8s.io/kubernetes/test/utils/image"
)

var _ = SIGDescribe("[Feature:DynamicAudit]", func() {
	f := framework.NewDefaultFramework("audit")
	BeforeEach(func() {
		framework.SkipUnlessProviderIs("gce")
	})

	// TODO(@pbarker): Get rid of [DisabledForLargeClusters] when feature request #53455 is ready.
	It("should dynamically audit API calls [DisabledForLargeClusters]", func() {
		namespace := f.Namespace.Name

		By("Creating a kubernetes client that impersonates an unauthorized anonymous user")
		config, err := framework.LoadConfig()
		framework.ExpectNoError(err, "failed to fetch config")

		config.Impersonate = restclient.ImpersonationConfig{
			UserName: "system:anonymous",
			Groups:   []string{"system:unauthenticated"},
		}
		anonymousClient, err := clientset.NewForConfig(config)
		framework.ExpectNoError(err, "failed to create the anonymous client")

		_, err = f.ClientSet.CoreV1().Namespaces().Create(&apiv1.Namespace{
			ObjectMeta: metav1.ObjectMeta{
				Name: "audit",
			},
		})
		framework.ExpectNoError(err, "failed to create namespace")

		_, err = f.ClientSet.CoreV1().Pods(namespace).Create(&apiv1.Pod{
			ObjectMeta: metav1.ObjectMeta{
				Name: "audit-proxy",
				Labels: map[string]string{
					"app": "audit",
				},
			},
			Spec: apiv1.PodSpec{
				Containers: []apiv1.Container{
					{
						Name:  "proxy",
						Image: imageutils.GetE2EImage(imageutils.AuditProxy),
						Ports: []apiv1.ContainerPort{
							{
								ContainerPort: 8080,
							},
						},
					},
				},
			},
		})
		framework.ExpectNoError(err, "failed to create proxy pod")

		_, err = f.ClientSet.CoreV1().Services(namespace).Create(&apiv1.Service{
			ObjectMeta: metav1.ObjectMeta{
				Name: "audit",
			},
			Spec: apiv1.ServiceSpec{
				Ports: []apiv1.ServicePort{
					{
						Port:       80,
						TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 8080},
					},
				},
				Selector: map[string]string{
					"app": "audit",
				},
			},
		})
		framework.ExpectNoError(err, "failed to create proxy service")

		config, err = framework.LoadConfig()
		framework.ExpectNoError(err, "failed to load config")

		var podIP string
		// get pod ip
		err = wait.Poll(100*time.Millisecond, 10*time.Second, func() (done bool, err error) {
			p, err := f.ClientSet.CoreV1().Pods(namespace).Get("audit-proxy", metav1.GetOptions{})
			if errors.IsNotFound(err) {
				framework.Logf("waiting for audit-proxy pod to be present")
				return false, nil
			} else if err != nil {
				return false, err
			}
			podIP = p.Status.PodIP
			if podIP == "" {
				framework.Logf("waiting for audit-proxy pod IP to be ready")
				return false, nil
			}
			return true, nil
		})
		framework.ExpectNoError(err, "timed out waiting for audit-proxy pod to be ready")

		podURL := fmt.Sprintf("http://%s:8080", podIP)
		// create audit sink
		sink := auditregv1alpha1.AuditSink{
			ObjectMeta: metav1.ObjectMeta{
				Name: "test",
			},
			Spec: auditregv1alpha1.AuditSinkSpec{
				Policy: auditregv1alpha1.Policy{
					Level: auditregv1alpha1.LevelRequestResponse,
					Stages: []auditregv1alpha1.Stage{
						auditregv1alpha1.StageRequestReceived,
						auditregv1alpha1.StageResponseStarted,
						auditregv1alpha1.StageResponseComplete,
						auditregv1alpha1.StagePanic,
					},
				},
				Webhook: auditregv1alpha1.Webhook{
					ClientConfig: auditregv1alpha1.WebhookClientConfig{
						URL: &podURL,
					},
				},
			},
		}

		_, err = f.ClientSet.AuditregistrationV1alpha1().AuditSinks().Create(&sink)
		framework.ExpectNoError(err, "failed to create audit sink")
		framework.Logf("created audit sink")

		// check that we are receiving logs in the proxy
		err = wait.Poll(100*time.Millisecond, 10*time.Second, func() (done bool, err error) {
			logs, err := framework.GetPodLogs(f.ClientSet, namespace, "audit-proxy", "proxy")
			if err != nil {
				framework.Logf("waiting for audit-proxy pod logs to be available")
				return false, nil
			}
			if logs == "" {
				framework.Logf("waiting for audit-proxy pod logs to be non-empty")
				return false, nil
			}
			return true, nil
		})
		framework.ExpectNoError(err, "failed to get logs from audit-proxy pod")

		auditTestUser = "kubernetes-admin"
		testCases := []struct {
			action func()
			events []utils.AuditEvent
		}{
			// Create, get, update, patch, delete, list, watch pods.
			// TODO(@pbarker): dedupe this with the main audit test once policy functionality is available
			// https://github.com/kubernetes/kubernetes/issues/70818
			{
				func() {
					pod := &apiv1.Pod{
						ObjectMeta: metav1.ObjectMeta{
							Name: "audit-pod",
						},
						Spec: apiv1.PodSpec{
							Containers: []apiv1.Container{{
								Name:  "pause",
								Image: imageutils.GetPauseImageName(),
							}},
						},
					}
					updatePod := func(pod *apiv1.Pod) {}

					f.PodClient().CreateSync(pod)

					_, err := f.PodClient().Get(pod.Name, metav1.GetOptions{})
					framework.ExpectNoError(err, "failed to get audit-pod")

					podChan, err := f.PodClient().Watch(watchOptions)
					framework.ExpectNoError(err, "failed to create watch for pods")
					for range podChan.ResultChan() {
					}

					f.PodClient().Update(pod.Name, updatePod)

					_, err = f.PodClient().List(metav1.ListOptions{})
					framework.ExpectNoError(err, "failed to list pods")

					_, err = f.PodClient().Patch(pod.Name, types.JSONPatchType, patch)
					framework.ExpectNoError(err, "failed to patch pod")

					f.PodClient().DeleteSync(pod.Name, &metav1.DeleteOptions{}, framework.DefaultPodDeletionTimeout)
				},
				[]utils.AuditEvent{
					{
						Level:             auditinternal.LevelRequestResponse,
						Stage:             auditinternal.StageResponseComplete,
						RequestURI:        fmt.Sprintf("/api/v1/namespaces/%s/pods", namespace),
						Verb:              "create",
						Code:              201,
						User:              auditTestUser,
						Resource:          "pods",
						Namespace:         namespace,
						RequestObject:     true,
						ResponseObject:    true,
						AuthorizeDecision: "allow",
					}, {
						Level:             auditinternal.LevelRequestResponse,
						Stage:             auditinternal.StageResponseComplete,
						RequestURI:        fmt.Sprintf("/api/v1/namespaces/%s/pods/audit-pod", namespace),
						Verb:              "get",
						Code:              200,
						User:              auditTestUser,
						Resource:          "pods",
						Namespace:         namespace,
						RequestObject:     false,
						ResponseObject:    true,
						AuthorizeDecision: "allow",
					}, {
						Level:             auditinternal.LevelRequestResponse,
						Stage:             auditinternal.StageResponseComplete,
						RequestURI:        fmt.Sprintf("/api/v1/namespaces/%s/pods", namespace),
						Verb:              "list",
						Code:              200,
						User:              auditTestUser,
						Resource:          "pods",
						Namespace:         namespace,
						RequestObject:     false,
						ResponseObject:    true,
						AuthorizeDecision: "allow",
					}, {
						Level:             auditinternal.LevelRequestResponse,
						Stage:             auditinternal.StageResponseStarted,
						RequestURI:        fmt.Sprintf("/api/v1/namespaces/%s/pods?timeout=%ds&timeoutSeconds=%d&watch=true", namespace, watchTestTimeout, watchTestTimeout),
						Verb:              "watch",
						Code:              200,
						User:              auditTestUser,
						Resource:          "pods",
						Namespace:         namespace,
						RequestObject:     false,
						ResponseObject:    false,
						AuthorizeDecision: "allow",
					}, {
						Level:             auditinternal.LevelRequestResponse,
						Stage:             auditinternal.StageResponseComplete,
						RequestURI:        fmt.Sprintf("/api/v1/namespaces/%s/pods?timeout=%ds&timeoutSeconds=%d&watch=true", namespace, watchTestTimeout, watchTestTimeout),
						Verb:              "watch",
						Code:              200,
						User:              auditTestUser,
						Resource:          "pods",
						Namespace:         namespace,
						RequestObject:     false,
						ResponseObject:    false,
						AuthorizeDecision: "allow",
					}, {
						Level:             auditinternal.LevelRequestResponse,
						Stage:             auditinternal.StageResponseComplete,
						RequestURI:        fmt.Sprintf("/api/v1/namespaces/%s/pods/audit-pod", namespace),
						Verb:              "update",
						Code:              200,
						User:              auditTestUser,
						Resource:          "pods",
						Namespace:         namespace,
						RequestObject:     true,
						ResponseObject:    true,
						AuthorizeDecision: "allow",
					}, {
						Level:             auditinternal.LevelRequestResponse,
						Stage:             auditinternal.StageResponseComplete,
						RequestURI:        fmt.Sprintf("/api/v1/namespaces/%s/pods/audit-pod", namespace),
						Verb:              "patch",
						Code:              200,
						User:              auditTestUser,
						Resource:          "pods",
						Namespace:         namespace,
						RequestObject:     true,
						ResponseObject:    true,
						AuthorizeDecision: "allow",
					}, {
						Level:             auditinternal.LevelRequestResponse,
						Stage:             auditinternal.StageResponseComplete,
						RequestURI:        fmt.Sprintf("/api/v1/namespaces/%s/pods/audit-pod", namespace),
						Verb:              "delete",
						Code:              200,
						User:              auditTestUser,
						Resource:          "pods",
						Namespace:         namespace,
						RequestObject:     true,
						ResponseObject:    true,
						AuthorizeDecision: "allow",
					},
				},
			},
		}

		// test authorizer annotations, RBAC is required.
		annotationTestCases := []struct {
			action func()
			events []utils.AuditEvent
		}{

			// get a pod with unauthorized user
			{
				func() {
					_, err := anonymousClient.CoreV1().Pods(namespace).Get("another-audit-pod", metav1.GetOptions{})
					expectForbidden(err)
				},
				[]utils.AuditEvent{
					{
						Level:              auditinternal.LevelRequestResponse,
						Stage:              auditinternal.StageResponseComplete,
						RequestURI:         fmt.Sprintf("/api/v1/namespaces/%s/pods/another-audit-pod", namespace),
						Verb:               "get",
						Code:               403,
						User:               auditTestUser,
						ImpersonatedUser:   "system:anonymous",
						ImpersonatedGroups: "system:unauthenticated",
						Resource:           "pods",
						Namespace:          namespace,
						RequestObject:      false,
						ResponseObject:     true,
						AuthorizeDecision:  "forbid",
					},
				},
			},
		}

		if framework.IsRBACEnabled(f) {
			testCases = append(testCases, annotationTestCases...)
		}
		expectedEvents := []utils.AuditEvent{}
		for _, t := range testCases {
			t.action()
			expectedEvents = append(expectedEvents, t.events...)
		}

		// The default flush timeout is 30 seconds, therefore it should be enough to retry once
		// to find all expected events. However, we're waiting for 5 minutes to avoid flakes.
		pollingInterval := 30 * time.Second
		pollingTimeout := 5 * time.Minute
		err = wait.Poll(pollingInterval, pollingTimeout, func() (bool, error) {
			// Fetch the logs
			logs, err := framework.GetPodLogs(f.ClientSet, namespace, "audit-proxy", "proxy")
			if err != nil {
				return false, err
			}
			reader := strings.NewReader(logs)
			missing, err := utils.CheckAuditLines(reader, expectedEvents, auditv1.SchemeGroupVersion)
			if err != nil {
				framework.Logf("Failed to observe audit events: %v", err)
			} else if len(missing) > 0 {
				framework.Logf("Events %#v not found!", missing)
			}
			return len(missing) == 0, nil
		})
		framework.ExpectNoError(err, "after %v failed to observe audit events", pollingTimeout)
		err = f.ClientSet.AuditregistrationV1alpha1().AuditSinks().Delete("test", &metav1.DeleteOptions{})
		framework.ExpectNoError(err, "could not delete audit configuration")
	})
})
<|endoftext|>


<commit_before>package operators

import (
	"fmt"

	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"

	exutil "github.com/openshift/origin/test/extended/util"
	e2e "k8s.io/kubernetes/test/e2e/framework"
)

var _ = g.Describe("[Feature:Platform] OLM should", func() {
	defer g.GinkgoRecover()

	var oc = exutil.NewCLI("olm", exutil.KubeConfigPath())

	operators := "operators.coreos.com"
	providedAPIs := []struct {
		fromAPIService bool
		group          string
		version        string
		plural         string
	}{
		{
			fromAPIService: true,
			group:          "packages." + operators,
			version:        "v1",
			plural:         "packagemanifests",
		},
		{
			group:   operators,
			version: "v1",
			plural:  "operatorgroups",
		},
		{
			group:   operators,
			version: "v1alpha1",
			plural:  "clusterserviceversions",
		},
		{
			group:   operators,
			version: "v1alpha1",
			plural:  "catalogsources",
		},
		{
			group:   operators,
			version: "v1alpha1",
			plural:  "installplans",
		},
		{
			group:   operators,
			version: "v1alpha1",
			plural:  "subscriptions",
		},
	}

	for _, api := range providedAPIs {
		g.It(fmt.Sprintf("be installed with %s at version %s", api.plural, api.version), func() {
			if api.fromAPIService {
				// Ensure spec.version matches expected
				raw, err := oc.AsAdmin().Run("get").Args("apiservices", fmt.Sprintf("%s.%s", api.version, api.group), "-o=jsonpath='{.spec.version}'").Output()
				o.Expect(err).NotTo(o.HaveOccurred())
				o.Expect(raw).To(o.Equal(api.version))
			} else {
				// Ensure expected version exists in spec.versions and is both served and stored
				raw, err := oc.AsAdmin().Run("get").Args("crds", fmt.Sprintf("%s.%s", api.plural, api.group), fmt.Sprintf("-o=jsonpath='{.spec.versions[?(@.name==\"%s\")]}'", api.version)).Output()
				o.Expect(err).NotTo(o.HaveOccurred())
				o.Expect(raw).To(o.ContainSubstring("served:true"))
				o.Expect(raw).To(o.ContainSubstring("storage:true"))
			}
		})
	}

	// OCP-24061 - [bz 1685230] OLM operator should use imagePullPolicy: IfNotPresent
	g.It("have imagePullPolicy:IfNotPresent on thier deployments", func() {
		var deploymentResource = [3]string{"catalog-operator", "olm-operator", "packageserver"}
		for _, v := range deploymentResource {
			msg, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("-n", "openshift-operator-lifecycle-manager", "deployment", v, "-o=jsonpath={.spec.template.spec.containers[*].imagePullPolicy}").Output()
			e2e.Logf("%s.imagePullPolicy:%s", v, msg)
			if err != nil {
				e2e.Failf("Unable to get %s, error:%v", msg, err)
			}
			o.Expect(err).NotTo(o.HaveOccurred())
			o.Expect(msg).To(o.Equal("IfNotPresent"))
		}
	})
})
<commit_msg>Adding author comment<commit_after>package operators

import (
	"fmt"

	g "github.com/onsi/ginkgo"
	o "github.com/onsi/gomega"

	exutil "github.com/openshift/origin/test/extended/util"
	e2e "k8s.io/kubernetes/test/e2e/framework"
)

var _ = g.Describe("[Feature:Platform] OLM should", func() {
	defer g.GinkgoRecover()

	var oc = exutil.NewCLI("olm", exutil.KubeConfigPath())

	operators := "operators.coreos.com"
	providedAPIs := []struct {
		fromAPIService bool
		group          string
		version        string
		plural         string
	}{
		{
			fromAPIService: true,
			group:          "packages." + operators,
			version:        "v1",
			plural:         "packagemanifests",
		},
		{
			group:   operators,
			version: "v1",
			plural:  "operatorgroups",
		},
		{
			group:   operators,
			version: "v1alpha1",
			plural:  "clusterserviceversions",
		},
		{
			group:   operators,
			version: "v1alpha1",
			plural:  "catalogsources",
		},
		{
			group:   operators,
			version: "v1alpha1",
			plural:  "installplans",
		},
		{
			group:   operators,
			version: "v1alpha1",
			plural:  "subscriptions",
		},
	}

	for _, api := range providedAPIs {
		g.It(fmt.Sprintf("be installed with %s at version %s", api.plural, api.version), func() {
			if api.fromAPIService {
				// Ensure spec.version matches expected
				raw, err := oc.AsAdmin().Run("get").Args("apiservices", fmt.Sprintf("%s.%s", api.version, api.group), "-o=jsonpath='{.spec.version}'").Output()
				o.Expect(err).NotTo(o.HaveOccurred())
				o.Expect(raw).To(o.Equal(api.version))
			} else {
				// Ensure expected version exists in spec.versions and is both served and stored
				raw, err := oc.AsAdmin().Run("get").Args("crds", fmt.Sprintf("%s.%s", api.plural, api.group), fmt.Sprintf("-o=jsonpath='{.spec.versions[?(@.name==\"%s\")]}'", api.version)).Output()
				o.Expect(err).NotTo(o.HaveOccurred())
				o.Expect(raw).To(o.ContainSubstring("served:true"))
				o.Expect(raw).To(o.ContainSubstring("storage:true"))
			}
		})
	}

	// OCP-24061 - [bz 1685230] OLM operator should use imagePullPolicy: IfNotPresent
	// author: bandrade@redhat.com
	g.It("have imagePullPolicy:IfNotPresent on thier deployments", func() {
		var deploymentResource = [3]string{"catalog-operator", "olm-operator", "packageserver"}
		for _, v := range deploymentResource {
			msg, err := oc.AsAdmin().WithoutNamespace().Run("get").Args("-n", "openshift-operator-lifecycle-manager", "deployment", v, "-o=jsonpath={.spec.template.spec.containers[*].imagePullPolicy}").Output()
			e2e.Logf("%s.imagePullPolicy:%s", v, msg)
			if err != nil {
				e2e.Failf("Unable to get %s, error:%v", msg, err)
			}
			o.Expect(err).NotTo(o.HaveOccurred())
			o.Expect(msg).To(o.Equal("IfNotPresent"))
		}
	})
})
<|endoftext|>


<commit_before>package bytepool

import (
  "io"
)

type Item struct {
  pool *Pool
  length int
  position int
  bytes []byte
}

func newItem(capacity int, pool *Pool) *Item {
  return &Item {
    pool: pool,
    bytes: make([]byte, capacity),
  }
}

func (item *Item) Write(b []byte) int {
  if item.Full() { return 0 }
  n := copy(item.bytes[item.length:], b)
  item.length += n
  return n
}

func (item *Item) WriteByte(b byte) bool {
  if item.Full() { return false }
  item.bytes[item.length] = b
  item.length += 1
  return true
}

func (item *Item) WriteString(s string) int {
  if item.Full() { return 0 }
  n := copy(item.bytes[item.length:], s)
  item.length += n
  return n
}

func (item *Item) ReadFrom(reader io.Reader) (int64, error) {
  var read int64
  for {
    r, err := reader.Read(item.bytes[item.length:])
    read += int64(r)
    item.length += r
    if err == io.EOF { return read, nil }
    if err != nil { return read, err }
  }
}

func (item *Item) Read(p []byte) (int, error) {
  if item.Drained() { return 0, io.EOF }
  n := copy(p, item.bytes[item.position:item.length])
  item.position += n
  if item.Drained() { return n, io.EOF }
  return n, nil
}

func (item *Item) Bytes() []byte {
  return item.bytes[0:item.length]
}

func (item *Item) String() string {
  return string(item.Bytes())
}

func (item *Item) Len() int {
  return item.length;
}

func (item *Item) Position(position int) {
  item.length = position
}

func (item *Item) Full() bool {
  return item.length == cap(item.bytes)
}

func (item *Item) Drained() bool {
  return item.length == item.position
}

func (item *Item) Close() error{
  item.length = 0
  if item.pool != nil {
    item.pool.list <- item
  }
  return nil
}
<commit_msg>close needs to also reset the read position<commit_after>package bytepool

import (
  "io"
)

type Item struct {
  pool *Pool
  length int
  position int
  bytes []byte
}

func newItem(capacity int, pool *Pool) *Item {
  return &Item {
    pool: pool,
    bytes: make([]byte, capacity),
  }
}

func (item *Item) Write(b []byte) int {
  if item.Full() { return 0 }
  n := copy(item.bytes[item.length:], b)
  item.length += n
  return n
}

func (item *Item) WriteByte(b byte) bool {
  if item.Full() { return false }
  item.bytes[item.length] = b
  item.length += 1
  return true
}

func (item *Item) WriteString(s string) int {
  if item.Full() { return 0 }
  n := copy(item.bytes[item.length:], s)
  item.length += n
  return n
}

func (item *Item) ReadFrom(reader io.Reader) (int64, error) {
  var read int64
  for {
    r, err := reader.Read(item.bytes[item.length:])
    read += int64(r)
    item.length += r
    if err == io.EOF { return read, nil }
    if err != nil { return read, err }
  }
}

func (item *Item) Read(p []byte) (int, error) {
  if item.Drained() { return 0, io.EOF }
  n := copy(p, item.bytes[item.position:item.length])
  item.position += n
  if item.Drained() { return n, io.EOF }
  return n, nil
}

func (item *Item) Bytes() []byte {
  return item.bytes[0:item.length]
}

func (item *Item) String() string {
  return string(item.Bytes())
}

func (item *Item) Len() int {
  return item.length;
}

func (item *Item) Position(position int) {
  item.length = position
}

func (item *Item) Full() bool {
  return item.length == cap(item.bytes)
}

func (item *Item) Drained() bool {
  return item.length == item.position
}

func (item *Item) Close() error{
  item.length = 0
  item.position = 0
  if item.pool != nil {
    item.pool.list <- item
  }
  return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 Factom Foundation
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package wallet

import (
	"fmt"

	"github.com/FactomProject/factom"
	"github.com/FactomProject/factomd/common/factoid"
	"github.com/FactomProject/factomd/common/interfaces"
	"github.com/FactomProject/factomd/common/primitives"
	"github.com/FactomProject/factomd/database/databaseOverlay"
	"github.com/FactomProject/factomd/database/hybridDB"
)

// Database keys and key prefixes
var (
	fblockDBPrefix = []byte("FBlock")
)

type TXDatabaseOverlay struct {
	dbo databaseOverlay.Overlay
}

func NewTXOverlay(db interfaces.IDatabase) *TXDatabaseOverlay {
	answer := new(TXDatabaseOverlay)
	answer.dbo.DB = db
	return answer
}

func NewTXLevelDB(ldbpath string) (*TXDatabaseOverlay, error) {
	db, err := hybridDB.NewLevelMapHybridDB(ldbpath, false)
	if err != nil {
		fmt.Printf("err opening transaction db: %v\n", err)
	}

	if db == nil {
		fmt.Println("Creating new transaction db ...")
		db, err = hybridDB.NewLevelMapHybridDB(ldbpath, true)

		if err != nil {
			return nil, err
		}
	}
	fmt.Println("Transaction database started from: " + ldbpath)
	return NewTXOverlay(db), nil
}

func (db *TXDatabaseOverlay) Close() error {
	return db.dbo.Close()
}

// GetAllTXs returns a list of all transactions in the history of Factom. A
// local database is used to cache the factoid blocks.
func (db *TXDatabaseOverlay) GetAllTXs() ([]interfaces.ITransaction, error) {
	// update the database and get the newest fblock
	newest, err := db.update()
	if err != nil {
		return nil, err
	}
	fblock, err := db.GetFBlock(newest)
	if err != nil {
		return nil, err
	}
	txs := make([]interfaces.ITransaction, 0)

	for {
		// get all of the txs from the block
		height := fblock.GetDatabaseHeight()
		for _, tx := range fblock.GetTransactions() {
			ins, err := tx.TotalInputs()
			if err != nil {
				return nil, err
			}
			outs, err := tx.TotalOutputs()
			if err != nil {
				return nil, err
			}

			if ins != 0 || outs != 0 {
				tx.SetBlockHeight(height)
				txs = append(txs, tx)
			}
		}

		if pre := fblock.GetPrevKeyMR().String(); pre != factom.ZeroHash {
			// get the previous block
			fblock, err = db.GetFBlock(pre)
			if err != nil {
				return nil, err
			} else if fblock == nil {
				return nil, fmt.Errorf("Missing fblock in database: %s", pre)
			}
		} else {
			break
		}
	}

	return txs, nil
}

// GetTX gets a transaction by the transaction id
func (db *TXDatabaseOverlay) GetTX(txid string) (
	interfaces.ITransaction, error) {
	txs, err := db.GetAllTXs()
	if err != nil {
		return nil, err
	}

	for _, tx := range txs {
		if tx.GetSigHash().String() == txid {
			return tx, nil
		}
	}

	return nil, fmt.Errorf("Transaction not found")
}

// GetTXAddress returns a list of all transactions in the history of Factom that
// include a specific address.
func (db *TXDatabaseOverlay) GetTXAddress(adr string) (
	[]interfaces.ITransaction, error) {
	filtered := make([]interfaces.ITransaction, 0)

	txs, err := db.GetAllTXs()
	if err != nil {
		return nil, err
	}

	for _, tx := range txs {
		for _, in := range tx.GetInputs() {
			if primitives.ConvertFctAddressToUserStr(
				in.GetAddress()) == adr {
				txs = append(filtered, tx)
			}
		}
		for _, out := range tx.GetOutputs() {
			if primitives.ConvertFctAddressToUserStr(
				out.GetAddress()) == adr {
				txs = append(filtered, tx)
			}
		}
	}

	return filtered, nil
}

func (db *TXDatabaseOverlay) GetTXRange(start, end int) (
	[]interfaces.ITransaction, error) {
	if start < 0 || end < 0 {
		return nil, fmt.Errorf("Range cannot have negative numbers")
	}
	s, e := uint32(start), uint32(end)

	filtered := make([]interfaces.ITransaction, 0)

	txs, err := db.GetAllTXs()
	if err != nil {
		return nil, err
	}

	for _, tx := range txs {
		if s <= tx.GetBlockHeight() && tx.GetBlockHeight() <= e {
			filtered = append(filtered, tx)
		}
	}

	return filtered, nil
}

// GetFBlock retrives a Factoid Block from Factom
func (db *TXDatabaseOverlay) GetFBlock(keymr string) (interfaces.IFBlock, error) {
	fblock := new(factoid.FBlock)
	data, err := db.dbo.Get(fblockDBPrefix, []byte(keymr), fblock)
	if err != nil {
		return nil, err
	}
	if data == nil {
		return nil, nil
	}
	return data.(interfaces.IFBlock), nil
}

func (db *TXDatabaseOverlay) InsertFBlock(fblock interfaces.IFBlock) error {
	fblockmr := []byte(fblock.GetKeyMR().String())

	batch := []interfaces.Record{}
	batch = append(batch, interfaces.Record{fblockDBPrefix, fblockmr, fblock})

	return db.dbo.PutInBatch(batch)
}

// update gets all fblocks written since the database was last updated, and
// returns the most recent fblock keymr.
func (db *TXDatabaseOverlay) update() (string, error) {
	fblock, err := fblockHead()
	if err != nil {
		return "", err
	}
	newest := fblock.GetKeyMR().String()

	prevmr := fblock.GetPrevKeyMR().String()
	for prevmr != factom.ZeroHash {
		//		// stop when we reach an fblock that is already in the db
		//		if f, err := db.GetFBlock(prevmr); err != nil {
		//			return "", err
		//		} else if f != nil {
		//			return newest, nil
		//		}

		// add the fblock to the db
		if err := db.InsertFBlock(fblock); err != nil {
			return "", err
		}

		fblock, err = getfblock(prevmr)
		if err != nil {
			return "", err
		}
		prevmr = fblock.GetPrevKeyMR().String()
	}

	// write the last fblock into the db
	if err := db.InsertFBlock(fblock); err != nil {
		return "", err
	}

	return newest, nil
}

// fblockHead gets the most recent fblock.
func fblockHead() (interfaces.IFBlock, error) {
	fblockID := "000000000000000000000000000000000000000000000000000000000000000f"

	dbhead, err := factom.GetDBlockHead()
	if err != nil {
		return nil, err
	}
	dblock, err := factom.GetDBlock(dbhead)
	if err != nil {
		return nil, err
	}

	var fblockmr string
	for _, eblock := range dblock.EntryBlockList {
		if eblock.ChainID == fblockID {
			fblockmr = eblock.KeyMR
		}
	}
	if fblockmr == "" {
		return nil, err
	}

	return getfblock(fblockmr)
}

func getfblock(keymr string) (interfaces.IFBlock, error) {
	p, err := factom.GetRaw(keymr)
	if err != nil {
		return nil, err
	}
	return factoid.UnmarshalFBlock(p)
}
<commit_msg>added bolt db functionality for txdb<commit_after>// Copyright 2016 Factom Foundation
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package wallet

import (
	"fmt"

	"github.com/FactomProject/factom"
	"github.com/FactomProject/factomd/common/factoid"
	"github.com/FactomProject/factomd/common/interfaces"
	"github.com/FactomProject/factomd/common/primitives"
	"github.com/FactomProject/factomd/database/databaseOverlay"
	"github.com/FactomProject/factomd/database/hybridDB"
)

// Database keys and key prefixes
var (
	fblockDBPrefix = []byte("FBlock")
)

type TXDatabaseOverlay struct {
	dbo databaseOverlay.Overlay
}

func NewTXOverlay(db interfaces.IDatabase) *TXDatabaseOverlay {
	answer := new(TXDatabaseOverlay)
	answer.dbo.DB = db
	return answer
}

func NewTXLevelDB(ldbpath string) (*TXDatabaseOverlay, error) {
	db, err := hybridDB.NewLevelMapHybridDB(ldbpath, false)
	if err != nil {
		fmt.Printf("err opening transaction db: %v\n", err)
	}

	if db == nil {
		fmt.Println("Creating new transaction db ...")
		db, err = hybridDB.NewLevelMapHybridDB(ldbpath, true)

		if err != nil {
			return nil, err
		}
	}
	fmt.Println("Transaction database started from: " + ldbpath)
	return NewTXOverlay(db), nil
}

func NewTXBoltDB(boltPath string) (*TXDatabaseOverlay, error) {
	db := hybridDB.NewBoltMapHybridDB(nil, boltPath)

	fmt.Println("Database started from: " + boltPath)
	return NewTXOverlay(db), nil
}

func (db *TXDatabaseOverlay) Close() error {
	return db.dbo.Close()
}

// GetAllTXs returns a list of all transactions in the history of Factom. A
// local database is used to cache the factoid blocks.
func (db *TXDatabaseOverlay) GetAllTXs() ([]interfaces.ITransaction, error) {
	// update the database and get the newest fblock
	newest, err := db.update()
	if err != nil {
		return nil, err
	}
	fblock, err := db.GetFBlock(newest)
	if err != nil {
		return nil, err
	}
	txs := make([]interfaces.ITransaction, 0)

	for {
		// get all of the txs from the block
		height := fblock.GetDatabaseHeight()
		for _, tx := range fblock.GetTransactions() {
			ins, err := tx.TotalInputs()
			if err != nil {
				return nil, err
			}
			outs, err := tx.TotalOutputs()
			if err != nil {
				return nil, err
			}

			if ins != 0 || outs != 0 {
				tx.SetBlockHeight(height)
				txs = append(txs, tx)
			}
		}

		if pre := fblock.GetPrevKeyMR().String(); pre != factom.ZeroHash {
			// get the previous block
			fblock, err = db.GetFBlock(pre)
			if err != nil {
				return nil, err
			} else if fblock == nil {
				return nil, fmt.Errorf("Missing fblock in database: %s", pre)
			}
		} else {
			break
		}
	}

	return txs, nil
}

// GetTX gets a transaction by the transaction id
func (db *TXDatabaseOverlay) GetTX(txid string) (
	interfaces.ITransaction, error) {
	txs, err := db.GetAllTXs()
	if err != nil {
		return nil, err
	}

	for _, tx := range txs {
		if tx.GetSigHash().String() == txid {
			return tx, nil
		}
	}

	return nil, fmt.Errorf("Transaction not found")
}

// GetTXAddress returns a list of all transactions in the history of Factom that
// include a specific address.
func (db *TXDatabaseOverlay) GetTXAddress(adr string) (
	[]interfaces.ITransaction, error) {
	filtered := make([]interfaces.ITransaction, 0)

	txs, err := db.GetAllTXs()
	if err != nil {
		return nil, err
	}

	for _, tx := range txs {
		for _, in := range tx.GetInputs() {
			if primitives.ConvertFctAddressToUserStr(
				in.GetAddress()) == adr {
				txs = append(filtered, tx)
			}
		}
		for _, out := range tx.GetOutputs() {
			if primitives.ConvertFctAddressToUserStr(
				out.GetAddress()) == adr {
				txs = append(filtered, tx)
			}
		}
	}

	return filtered, nil
}

func (db *TXDatabaseOverlay) GetTXRange(start, end int) (
	[]interfaces.ITransaction, error) {
	if start < 0 || end < 0 {
		return nil, fmt.Errorf("Range cannot have negative numbers")
	}
	s, e := uint32(start), uint32(end)

	filtered := make([]interfaces.ITransaction, 0)

	txs, err := db.GetAllTXs()
	if err != nil {
		return nil, err
	}

	for _, tx := range txs {
		if s <= tx.GetBlockHeight() && tx.GetBlockHeight() <= e {
			filtered = append(filtered, tx)
		}
	}

	return filtered, nil
}

// GetFBlock retrives a Factoid Block from Factom
func (db *TXDatabaseOverlay) GetFBlock(keymr string) (interfaces.IFBlock, error) {
	fblock := new(factoid.FBlock)
	data, err := db.dbo.Get(fblockDBPrefix, []byte(keymr), fblock)
	if err != nil {
		return nil, err
	}
	if data == nil {
		return nil, nil
	}
	return data.(interfaces.IFBlock), nil
}

func (db *TXDatabaseOverlay) InsertFBlock(fblock interfaces.IFBlock) error {
	fblockmr := []byte(fblock.GetKeyMR().String())

	batch := []interfaces.Record{}
	batch = append(batch, interfaces.Record{fblockDBPrefix, fblockmr, fblock})

	return db.dbo.PutInBatch(batch)
}

// update gets all fblocks written since the database was last updated, and
// returns the most recent fblock keymr.
func (db *TXDatabaseOverlay) update() (string, error) {
	fblock, err := fblockHead()
	if err != nil {
		return "", err
	}
	newest := fblock.GetKeyMR().String()

	prevmr := fblock.GetPrevKeyMR().String()
	for prevmr != factom.ZeroHash {
		//		// stop when we reach an fblock that is already in the db
		//		if f, err := db.GetFBlock(prevmr); err != nil {
		//			return "", err
		//		} else if f != nil {
		//			return newest, nil
		//		}

		// add the fblock to the db
		if err := db.InsertFBlock(fblock); err != nil {
			return "", err
		}

		fblock, err = getfblock(prevmr)
		if err != nil {
			return "", err
		}
		prevmr = fblock.GetPrevKeyMR().String()
	}

	// write the last fblock into the db
	if err := db.InsertFBlock(fblock); err != nil {
		return "", err
	}

	return newest, nil
}

// fblockHead gets the most recent fblock.
func fblockHead() (interfaces.IFBlock, error) {
	fblockID := "000000000000000000000000000000000000000000000000000000000000000f"

	dbhead, err := factom.GetDBlockHead()
	if err != nil {
		return nil, err
	}
	dblock, err := factom.GetDBlock(dbhead)
	if err != nil {
		return nil, err
	}

	var fblockmr string
	for _, eblock := range dblock.EntryBlockList {
		if eblock.ChainID == fblockID {
			fblockmr = eblock.KeyMR
		}
	}
	if fblockmr == "" {
		return nil, err
	}

	return getfblock(fblockmr)
}

func getfblock(keymr string) (interfaces.IFBlock, error) {
	p, err := factom.GetRaw(keymr)
	if err != nil {
		return nil, err
	}
	return factoid.UnmarshalFBlock(p)
}
<|endoftext|>


<commit_before>// Copyright 2016 Factom Foundation
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package wallet

import (
	"encoding/hex"
	"fmt"

	"github.com/FactomProject/factom"
	"github.com/FactomProject/factomd/common/factoid"
	"github.com/FactomProject/factomd/common/interfaces"
	"github.com/FactomProject/factomd/common/primitives"
	"github.com/FactomProject/factomd/database/databaseOverlay"
	"github.com/FactomProject/factomd/database/hybridDB"
	"os"
)

// Database keys and key prefixes
var (
	fblockDBPrefix = []byte("FBlock")
)

type TXDatabaseOverlay struct {
	dbo databaseOverlay.Overlay
}

func NewTXOverlay(db interfaces.IDatabase) *TXDatabaseOverlay {
	answer := new(TXDatabaseOverlay)
	answer.dbo.DB = db
	return answer
}

func NewTXLevelDB(ldbpath string) (*TXDatabaseOverlay, error) {
	db, err := hybridDB.NewLevelMapHybridDB(ldbpath, false)
	if err != nil {
		fmt.Printf("err opening transaction db: %v\n", err)
	}

	if db == nil {
		fmt.Println("Creating new transaction db ...")
		db, err = hybridDB.NewLevelMapHybridDB(ldbpath, true)

		if err != nil {
			return nil, err
		}
	}
	fmt.Println("Transaction database started from: " + ldbpath)
	return NewTXOverlay(db), nil
}

func NewTXBoltDB(boltPath string) (*TXDatabaseOverlay, error) {
	fileInfo, err := os.Stat(boltPath)
	if err == nil { //if it exists
		if fileInfo.IsDir() { //if it is a folder though
			return nil, fmt.Errorf("The path %s is a directory.  Please specify a file name.", boltPath)
		}
	}
	if err != nil && !os.IsNotExist(err) { //some other error, besides the file not existing
		fmt.Printf("database error %s\n", err)
		return nil, err
	}

	defer func() {
		if r := recover(); r != nil {
			fmt.Printf("Could not use wallet cache database file \"%s\"\n%v\n", boltPath, r)
			os.Exit(1)
		}
	}()
	db := hybridDB.NewBoltMapHybridDB(nil, boltPath)

	fmt.Println("Database started from: " + boltPath)
	return NewTXOverlay(db), nil
}

func (db *TXDatabaseOverlay) Close() error {
	return db.dbo.Close()
}

// GetAllTXs returns a list of all transactions in the history of Factom. A
// local database is used to cache the factoid blocks.
func (db *TXDatabaseOverlay) GetAllTXs() ([]interfaces.ITransaction, error) {
	// update the database and get the newest fblock
	newest, err := db.update()
	if err != nil {
		return nil, err
	}
	fblock, err := db.GetFBlock(newest)
	if err != nil {
		return nil, err
	}
	txs := make([]interfaces.ITransaction, 0)

	for {
		// get all of the txs from the block
		height := fblock.GetDatabaseHeight()
		for _, tx := range fblock.GetTransactions() {
			ins, err := tx.TotalInputs()
			if err != nil {
				return nil, err
			}
			outs, err := tx.TotalOutputs()
			if err != nil {
				return nil, err
			}

			if ins != 0 || outs != 0 {
				tx.SetBlockHeight(height)
				txs = append(txs, tx)
			}
		}

		if pre := fblock.GetPrevKeyMR().String(); pre != factom.ZeroHash {
			// get the previous block
			fblock, err = db.GetFBlock(pre)
			if err != nil {
				return nil, err
			} else if fblock == nil {
				return nil, fmt.Errorf("Missing fblock in database: %s", pre)
			}
		} else {
			break
		}
	}

	return txs, nil
}

// GetTX gets a transaction by the transaction id
func (db *TXDatabaseOverlay) GetTX(txid string) (
	interfaces.ITransaction, error) {
	txs, err := db.GetAllTXs()
	if err != nil {
		return nil, err
	}

	for _, tx := range txs {
		if tx.GetSigHash().String() == txid {
			return tx, nil
		}
	}

	return nil, fmt.Errorf("Transaction not found")
}

// GetTXAddress returns a list of all transactions in the history of Factom that
// include a specific address.
func (db *TXDatabaseOverlay) GetTXAddress(adr string) (
	[]interfaces.ITransaction, error) {
	filtered := make([]interfaces.ITransaction, 0)

	txs, err := db.GetAllTXs()
	if err != nil {
		return nil, err
	}

	if factom.AddressStringType(adr) == factom.FactoidPub {
		for _, tx := range txs {
			for _, in := range tx.GetInputs() {
				if primitives.ConvertFctAddressToUserStr(in.GetAddress()) == adr {
					filtered = append(filtered, tx)
				}
			}
			for _, out := range tx.GetOutputs() {
				if primitives.ConvertFctAddressToUserStr(out.GetAddress()) == adr {
					filtered = append(filtered, tx)
				}
			}
		}
	} else if factom.AddressStringType(adr) == factom.ECPub {
		for _, tx := range txs {
			for _, out := range tx.GetECOutputs() {
				if primitives.ConvertECAddressToUserStr(out.GetAddress()) == adr {
					filtered = append(filtered, tx)
				}
			}
		}
	} else {
		return nil, fmt.Errorf("not a valid address")
	}

	return filtered, nil
}

func (db *TXDatabaseOverlay) GetTXRange(start, end int) (
	[]interfaces.ITransaction, error) {
	if start < 0 || end < 0 {
		return nil, fmt.Errorf("Range cannot have negative numbers")
	}
	s, e := uint32(start), uint32(end)

	filtered := make([]interfaces.ITransaction, 0)

	txs, err := db.GetAllTXs()
	if err != nil {
		return nil, err
	}

	for _, tx := range txs {
		if s <= tx.GetBlockHeight() && tx.GetBlockHeight() <= e {
			filtered = append(filtered, tx)
		}
	}

	return filtered, nil
}

// GetFBlock retrives a Factoid Block from Factom
func (db *TXDatabaseOverlay) GetFBlock(keymr string) (interfaces.IFBlock, error) {
	h, err := primitives.NewShaHashFromStr(keymr)
	if err != nil {
		return nil, err
	}

	fBlock, err := db.dbo.FetchFBlock(h)
	if err != nil {
		return nil, err
	}
	return fBlock, nil
}

func (db *TXDatabaseOverlay) FetchNextFBlockHeight() (uint32, error) {
	block, err := db.dbo.FetchFBlockHead()
	if err != nil {
		return 0, err
	}
	if block == nil {
		return 0, nil
	}
	return block.GetDBHeight() + 1, nil
}

func (db *TXDatabaseOverlay) InsertFBlockHead(fblock interfaces.IFBlock) error {
	return db.dbo.SaveFactoidBlockHead(fblock)
}

// update gets all fblocks written since the database was last updated, and
// returns the most recent fblock keymr.
func (db *TXDatabaseOverlay) update() (string, error) {
	newestFBlock, err := fblockHead()
	if err != nil {
		return "", err
	}

	newestHeight := newestFBlock.GetDatabaseHeight()
	start, err := db.FetchNextFBlockHeight()
	if err != nil {
		return "", err
	}

	if start >= newestHeight {
		return newestFBlock.GetKeyMR().String(), nil
	}

	for i := start; i <= newestHeight; i++ {
		if i%1000 == 0 {
			if newestHeight-start > 1000 {
				fmt.Printf("Fetching block %v / %v\n", i, newestHeight)
			}
		}
		fblock, err := getfblockbyheight(i)
		if err != nil {
			return "", err
		}
		db.InsertFBlockHead(fblock)
	}

	return newestFBlock.GetKeyMR().String(), nil
}

// fblockHead gets the most recent fblock.
func fblockHead() (interfaces.IFBlock, error) {
	fblockID := "000000000000000000000000000000000000000000000000000000000000000f"

	dbhead, err := factom.GetDBlockHead()
	if err != nil {
		return nil, err
	}
	dblock, err := factom.GetDBlock(dbhead)
	if err != nil {
		return nil, err
	}

	var fblockmr string
	for _, eblock := range dblock.EntryBlockList {
		if eblock.ChainID == fblockID {
			fblockmr = eblock.KeyMR
		}
	}
	if fblockmr == "" {
		return nil, err
	}

	return getfblock(fblockmr)
}

func getfblock(keymr string) (interfaces.IFBlock, error) {
	p, err := factom.GetRaw(keymr)
	if err != nil {
		return nil, err
	}
	return factoid.UnmarshalFBlock(p)
}

func getfblockbyheight(height uint32) (interfaces.IFBlock, error) {
	p, err := factom.GetFBlockByHeight(int64(height))
	if err != nil {
		return nil, err
	}
	h, err := hex.DecodeString(p.RawData)
	if err != nil {
		return nil, err
	}
	return factoid.UnmarshalFBlock(h)
}
<commit_msg>Fixed some database error.<commit_after>// Copyright 2016 Factom Foundation
// Use of this source code is governed by the MIT
// license that can be found in the LICENSE file.

package wallet

import (
	"encoding/hex"
	"fmt"

	"github.com/FactomProject/factom"
	"github.com/FactomProject/factomd/common/factoid"
	"github.com/FactomProject/factomd/common/interfaces"
	"github.com/FactomProject/factomd/common/primitives"
	"github.com/FactomProject/factomd/database/databaseOverlay"
	"github.com/FactomProject/factomd/database/hybridDB"
	"os"
)

// Database keys and key prefixes
var (
	fblockDBPrefix = []byte("FBlock")
)

type TXDatabaseOverlay struct {
	dbo databaseOverlay.Overlay
}

func NewTXOverlay(db interfaces.IDatabase) *TXDatabaseOverlay {
	answer := new(TXDatabaseOverlay)
	answer.dbo.DB = db
	return answer
}

func NewTXLevelDB(ldbpath string) (*TXDatabaseOverlay, error) {
	db, err := hybridDB.NewLevelMapHybridDB(ldbpath, false)
	if err != nil {
		fmt.Printf("err opening transaction db: %v\n", err)
	}

	if db == nil {
		fmt.Println("Creating new transaction db ...")
		db, err = hybridDB.NewLevelMapHybridDB(ldbpath, true)

		if err != nil {
			return nil, err
		}
	}
	fmt.Println("Transaction database started from: " + ldbpath)
	return NewTXOverlay(db), nil
}

func NewTXBoltDB(boltPath string) (*TXDatabaseOverlay, error) {
	fileInfo, err := os.Stat(boltPath)
	if err == nil { //if it exists
		if fileInfo.IsDir() { //if it is a folder though
			return nil, fmt.Errorf("The path %s is a directory.  Please specify a file name.", boltPath)
		}
	}
	if err != nil && !os.IsNotExist(err) { //some other error, besides the file not existing
		fmt.Printf("database error %s\n", err)
		return nil, err
	}

	defer func() {
		if r := recover(); r != nil {
			fmt.Printf("Could not use wallet cache database file \"%s\"\n%v\n", boltPath, r)
			os.Exit(1)
		}
	}()
	db := hybridDB.NewBoltMapHybridDB(nil, boltPath)

	fmt.Println("Database started from: " + boltPath)
	return NewTXOverlay(db), nil
}

func (db *TXDatabaseOverlay) Close() error {
	return db.dbo.Close()
}

// GetAllTXs returns a list of all transactions in the history of Factom. A
// local database is used to cache the factoid blocks.
func (db *TXDatabaseOverlay) GetAllTXs() ([]interfaces.ITransaction, error) {
	// update the database and get the newest fblock
	_, err := db.update()
	if err != nil {
		return nil, err
	}
	fblock, err := db.dbo.FetchFBlockHead()
	if err != nil {
		return nil, err
	}
	txs := make([]interfaces.ITransaction, 0)

	for {
		// get all of the txs from the block
		height := fblock.GetDatabaseHeight()
		for _, tx := range fblock.GetTransactions() {
			ins, err := tx.TotalInputs()
			if err != nil {
				return nil, err
			}
			outs, err := tx.TotalOutputs()
			if err != nil {
				return nil, err
			}

			if ins != 0 || outs != 0 {
				tx.SetBlockHeight(height)
				txs = append(txs, tx)
			}
		}

		if pre := fblock.GetPrevKeyMR().String(); pre != factom.ZeroHash {
			// get the previous block
			fblock, err = db.GetFBlock(pre)
			if err != nil {
				return nil, err
			} else if fblock == nil {
				return nil, fmt.Errorf("Missing fblock in database: %s", pre)
			}
		} else {
			break
		}
	}

	return txs, nil
}

// GetTX gets a transaction by the transaction id
func (db *TXDatabaseOverlay) GetTX(txid string) (
	interfaces.ITransaction, error) {
	txs, err := db.GetAllTXs()
	if err != nil {
		return nil, err
	}

	for _, tx := range txs {
		if tx.GetSigHash().String() == txid {
			return tx, nil
		}
	}

	return nil, fmt.Errorf("Transaction not found")
}

// GetTXAddress returns a list of all transactions in the history of Factom that
// include a specific address.
func (db *TXDatabaseOverlay) GetTXAddress(adr string) (
	[]interfaces.ITransaction, error) {
	filtered := make([]interfaces.ITransaction, 0)

	txs, err := db.GetAllTXs()
	if err != nil {
		return nil, err
	}

	if factom.AddressStringType(adr) == factom.FactoidPub {
		for _, tx := range txs {
			for _, in := range tx.GetInputs() {
				if primitives.ConvertFctAddressToUserStr(in.GetAddress()) == adr {
					filtered = append(filtered, tx)
				}
			}
			for _, out := range tx.GetOutputs() {
				if primitives.ConvertFctAddressToUserStr(out.GetAddress()) == adr {
					filtered = append(filtered, tx)
				}
			}
		}
	} else if factom.AddressStringType(adr) == factom.ECPub {
		for _, tx := range txs {
			for _, out := range tx.GetECOutputs() {
				if primitives.ConvertECAddressToUserStr(out.GetAddress()) == adr {
					filtered = append(filtered, tx)
				}
			}
		}
	} else {
		return nil, fmt.Errorf("not a valid address")
	}

	return filtered, nil
}

func (db *TXDatabaseOverlay) GetTXRange(start, end int) (
	[]interfaces.ITransaction, error) {
	if start < 0 || end < 0 {
		return nil, fmt.Errorf("Range cannot have negative numbers")
	}
	s, e := uint32(start), uint32(end)

	filtered := make([]interfaces.ITransaction, 0)

	txs, err := db.GetAllTXs()
	if err != nil {
		return nil, err
	}

	for _, tx := range txs {
		if s <= tx.GetBlockHeight() && tx.GetBlockHeight() <= e {
			filtered = append(filtered, tx)
		}
	}

	return filtered, nil
}

// GetFBlock retrives a Factoid Block from Factom
func (db *TXDatabaseOverlay) GetFBlock(keymr string) (interfaces.IFBlock, error) {
	h, err := primitives.NewShaHashFromStr(keymr)
	if err != nil {
		return nil, err
	}

	fBlock, err := db.dbo.FetchFBlock(h)
	if err != nil {
		return nil, err
	}
	return fBlock, nil
}

func (db *TXDatabaseOverlay) FetchNextFBlockHeight() (uint32, error) {
	block, err := db.dbo.FetchFBlockHead()
	if err != nil {
		return 0, err
	}
	if block == nil {
		return 0, nil
	}
	return block.GetDBHeight() + 1, nil
}

func (db *TXDatabaseOverlay) InsertFBlockHead(fblock interfaces.IFBlock) error {
	return db.dbo.SaveFactoidBlockHead(fblock)
}

// update gets all fblocks written since the database was last updated, and
// returns the most recent fblock keymr.
func (db *TXDatabaseOverlay) update() (string, error) {
	newestFBlock, err := fblockHead()
	if err != nil {
		return "", err
	}

	start, err := db.FetchNextFBlockHeight()
	if err != nil {
		return "", err
	}

	//Making sure we didn't switch networks
	genesis, err := db.dbo.FetchFBlockByHeight(0)
	if err != nil {
		return "", err
	}
	if genesis != nil {
		genesis2, err := getfblockbyheight(0)
		if err != nil {
			return "", err
		}
		if !genesis2.GetKeyMR().IsSameAs(genesis.GetKeyMR()) {
			start = 0
		}
	}

	newestHeight := newestFBlock.GetDatabaseHeight()

	if start >= newestHeight {
		return newestFBlock.GetKeyMR().String(), nil
	}

	for i := start; i <= newestHeight; i++ {
		if i%1000 == 0 {
			if newestHeight-start > 1000 {
				fmt.Printf("Fetching block %v / %v\n", i, newestHeight)
			}
		}
		fblock, err := getfblockbyheight(i)
		if err != nil {
			return "", err
		}
		db.InsertFBlockHead(fblock)
	}

	return newestFBlock.GetKeyMR().String(), nil
}

// fblockHead gets the most recent fblock.
func fblockHead() (interfaces.IFBlock, error) {
	fblockID := "000000000000000000000000000000000000000000000000000000000000000f"

	dbhead, err := factom.GetDBlockHead()
	if err != nil {
		return nil, err
	}
	dblock, err := factom.GetDBlock(dbhead)
	if err != nil {
		return nil, err
	}

	var fblockmr string
	for _, eblock := range dblock.EntryBlockList {
		if eblock.ChainID == fblockID {
			fblockmr = eblock.KeyMR
		}
	}
	if fblockmr == "" {
		return nil, err
	}

	return getfblock(fblockmr)
}

func getfblock(keymr string) (interfaces.IFBlock, error) {
	p, err := factom.GetRaw(keymr)
	if err != nil {
		return nil, err
	}
	return factoid.UnmarshalFBlock(p)
}

func getfblockbyheight(height uint32) (interfaces.IFBlock, error) {
	p, err := factom.GetFBlockByHeight(int64(height))
	if err != nil {
		return nil, err
	}
	h, err := hex.DecodeString(p.RawData)
	if err != nil {
		return nil, err
	}
	return factoid.UnmarshalFBlock(h)
}
<|endoftext|>


<commit_before>package sshagent

import (
	"github.com/Cloud-Foundations/golib/pkg/log"
)

func InsertCertIntoAgent(
	certText []byte,
	privateKey interface{},
	comment string,
	lifeTimeSecs uint32,
	logger log.Logger) error {
	return insertCertIntoAgent(certText, privateKey, comment, lifeTimeSecs, logger)
}
<commit_msg>missing file<commit_after>package sshagent

import (
	"github.com/Cloud-Foundations/golib/pkg/log"
)

func UpsertCertIntoAgent(
	certText []byte,
	privateKey interface{},
	comment string,
	lifeTimeSecs uint32,
	logger log.Logger) error {
	return upsertCertIntoAgent(certText, privateKey, comment, lifeTimeSecs, logger)
}
<|endoftext|>


<commit_before>package data

import (
	"testing"
)

func TestEmptyMessage(t *testing.T) {
	m := Empty()
	if m.String() != Encode(nil) {
		t.Fatalf("%v != %v", m.String(), Encode(nil))
	}
}

func TestSetMessage(t *testing.T) {
	m := Empty().Set("foo", "bar")
	output := m.String()
	expectedOutput := "000;3:foo,6:3:bar,,"
	if output != expectedOutput {
		t.Fatalf("'%v' != '%v'", output, expectedOutput)
	}
	decodedOutput, err := Decode(output)
	if err != nil {
		t.Fatal(err)
	}
	if len(decodedOutput) != 1 {
		t.Fatalf("wrong output data: %#v\n", decodedOutput)
	}
}

func TestSetMessageTwice(t *testing.T) {
	m := Empty().Set("foo", "bar").Set("ga", "bu")
	output := m.String()
	expectedOutput := "000;3:foo,6:3:bar,,2:ga,5:2:bu,,"
	if output != expectedOutput {
		t.Fatalf("'%v' != '%v'", output, expectedOutput)
	}
	decodedOutput, err := Decode(output)
	if err != nil {
		t.Fatal(err)
	}
	if len(decodedOutput) != 2 {
		t.Fatalf("wrong output data: %#v\n", decodedOutput)
	}
}

func TestSetDelMessage(t *testing.T) {
	m := Empty().Set("foo", "bar").Del("foo")
	output := m.String()
	expectedOutput := Encode(nil)
	if output != expectedOutput {
		t.Fatalf("'%v' != '%v'", output, expectedOutput)
	}
}
<commit_msg>Fix TestSetMessageTwice failing because map is unordered<commit_after>package data

import (
	"testing"
)

func TestEmptyMessage(t *testing.T) {
	m := Empty()
	if m.String() != Encode(nil) {
		t.Fatalf("%v != %v", m.String(), Encode(nil))
	}
}

func TestSetMessage(t *testing.T) {
	m := Empty().Set("foo", "bar")
	output := m.String()
	expectedOutput := "000;3:foo,6:3:bar,,"
	if output != expectedOutput {
		t.Fatalf("'%v' != '%v'", output, expectedOutput)
	}
	decodedOutput, err := Decode(output)
	if err != nil {
		t.Fatal(err)
	}
	if len(decodedOutput) != 1 {
		t.Fatalf("wrong output data: %#v\n", decodedOutput)
	}
	if len(decodedOutput["foo"]) != 1 {
		t.Fatalf("Key foo contains %d values, expected 1", len(decodedOutput["foo"]))
	}
	if decodedOutput["foo"][0] != "bar" {
		t.Fatalf("Key foo is %v, expected bar", decodedOutput["foo"][0])
	}
}

func TestSetMessageTwice(t *testing.T) {
	m := Empty().Set("foo", "bar").Set("ga", "bu")
	output := m.String()
	// Check two outputs because map is unordered
	expectedOutput1 := "000;3:foo,6:3:bar,,2:ga,5:2:bu,,"
	expectedOutput2 := "000;2:ga,5:2:bu,,3:foo,6:3:bar,,"
	if output != expectedOutput1 && output != expectedOutput2 {
		t.Fatalf("'%v' != '%v' and '%v' != '%v'", output, expectedOutput1, output, expectedOutput2)
	}
	decodedOutput, err := Decode(output)
	if err != nil {
		t.Fatal(err)
	}
	if len(decodedOutput) != 2 {
		t.Fatalf("wrong output data: %#v\n", decodedOutput)
	}
	if len(decodedOutput["foo"]) != 1 {
		t.Fatalf("Key foo contains %d values, expected 1", len(decodedOutput["foo"]))
	}
	if len(decodedOutput["ga"]) != 1 {
		t.Fatalf("Key ga contains %d values, expected 1", len(decodedOutput["ga"]))
	}
	if decodedOutput["foo"][0] != "bar" {
		t.Fatalf("Key foo is %v, expected bar", decodedOutput["foo"][0])
	}
	if decodedOutput["ga"][0] != "bu" {
		t.Fatalf("Key ga is %v, expected bu", decodedOutput["ga"][0])
	}
}

func TestSetDelMessage(t *testing.T) {
	m := Empty().Set("foo", "bar").Del("foo")
	output := m.String()
	expectedOutput := Encode(nil)
	if output != expectedOutput {
		t.Fatalf("'%v' != '%v'", output, expectedOutput)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"appstax-cli/appstax/commands"
	"appstax-cli/appstax/log"
	"appstax-cli/appstax/term"
	"fmt"
	"github.com/codegangsta/cli"
	"os"
	"os/signal"
	"strings"
)

func main() {
	setupSignals()
	cliApp := setupCli()
	log.Infof("Command to execute: %s", strings.Join(os.Args, " "))
	term.Section()
	cliApp.Run(os.Args)
	term.Section()
}

func setupCli() *cli.App {
	app := cli.NewApp()
	app.Name = "appstax"
	app.Usage = "command line interface for appstax.com"
	app.Version = "1.1.1"

	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "verbose",
			Usage: "enable verbose output",
		},
		cli.StringFlag{
			Name:  "baseurl",
			Usage: "set api base url",
		},
	}

	app.Commands = []cli.Command{
		{
			Name:   "signup",
			Usage:  "Create new account",
			Action: commands.DoSignup,
			Flags:  app.Flags,
		},
		{
			Name:   "login",
			Usage:  "Login",
			Action: commands.DoLogin,
			Flags:  app.Flags,
		},
		{
			Name:   "init",
			Usage:  "Initialize current directory as an appstax app",
			Action: commands.DoInit,
			Flags:  app.Flags,
		},
		{
			Name:   "info",
			Usage:  "Info about app configured in current directory",
			Action: commands.DoInfo,
			Flags:  app.Flags,
		},
		{
			Name:   "deploy",
			Usage:  "Deploy local files to <yourapp>.appstax.io",
			Action: commands.DoDeploy,
			Flags:  app.Flags,
		},
		{
			Name:   "open",
			Usage:  "Open your browser to the specified destination",
			Action: commands.DoOpen,
			Flags:  app.Flags,
		},
		{
			Name:   "logout",
			Usage:  "Logout of current app session",
			Action: commands.DoLogout,
			Flags:  app.Flags,
		},
		{
			Name:   "serve",
			Usage:  "Run development http server",
			Action: commands.DoServe,
			Flags:  app.Flags,
		},
		{
			Name:   "collection",
			Usage:  "Create and view collections",
			Action: commands.DoCollection,
			Flags:  app.Flags,
		},
		{
			Name:   "find",
			Usage:  "Get objects from a collection",
			Action: commands.DoFind,
			Flags:  app.Flags,
		},
		{
			Name:   "relation",
			Usage:  "Create and view relations",
			Action: commands.DoRelation,
			Flags:  app.Flags,
		},
	}

	return app
}

func setupSignals() {
	c := make(chan os.Signal, 10)
	signal.Notify(c, os.Interrupt)
	go func() {
		for _ = range c {
			fmt.Print("\n")
			os.Exit(-1)
		}
	}()
}
<commit_msg>Version 1.1.2<commit_after>package main

import (
	"appstax-cli/appstax/commands"
	"appstax-cli/appstax/log"
	"appstax-cli/appstax/term"
	"fmt"
	"github.com/codegangsta/cli"
	"os"
	"os/signal"
	"strings"
)

func main() {
	setupSignals()
	cliApp := setupCli()
	log.Infof("Command to execute: %s", strings.Join(os.Args, " "))
	term.Section()
	cliApp.Run(os.Args)
	term.Section()
}

func setupCli() *cli.App {
	app := cli.NewApp()
	app.Name = "appstax"
	app.Usage = "command line interface for appstax.com"
	app.Version = "1.1.2"

	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:  "verbose",
			Usage: "enable verbose output",
		},
		cli.StringFlag{
			Name:  "baseurl",
			Usage: "set api base url",
		},
	}

	app.Commands = []cli.Command{
		{
			Name:   "signup",
			Usage:  "Create new account",
			Action: commands.DoSignup,
			Flags:  app.Flags,
		},
		{
			Name:   "login",
			Usage:  "Login",
			Action: commands.DoLogin,
			Flags:  app.Flags,
		},
		{
			Name:   "init",
			Usage:  "Initialize current directory as an appstax app",
			Action: commands.DoInit,
			Flags:  app.Flags,
		},
		{
			Name:   "info",
			Usage:  "Info about app configured in current directory",
			Action: commands.DoInfo,
			Flags:  app.Flags,
		},
		{
			Name:   "deploy",
			Usage:  "Deploy local files to <yourapp>.appstax.io",
			Action: commands.DoDeploy,
			Flags:  app.Flags,
		},
		{
			Name:   "open",
			Usage:  "Open your browser to the specified destination",
			Action: commands.DoOpen,
			Flags:  app.Flags,
		},
		{
			Name:   "logout",
			Usage:  "Logout of current app session",
			Action: commands.DoLogout,
			Flags:  app.Flags,
		},
		{
			Name:   "serve",
			Usage:  "Run development http server",
			Action: commands.DoServe,
			Flags:  app.Flags,
		},
		{
			Name:   "collection",
			Usage:  "Create and view collections",
			Action: commands.DoCollection,
			Flags:  app.Flags,
		},
		{
			Name:   "find",
			Usage:  "Get objects from a collection",
			Action: commands.DoFind,
			Flags:  app.Flags,
		},
		{
			Name:   "relation",
			Usage:  "Create and view relations",
			Action: commands.DoRelation,
			Flags:  app.Flags,
		},
	}

	return app
}

func setupSignals() {
	c := make(chan os.Signal, 10)
	signal.Notify(c, os.Interrupt)
	go func() {
		for _ = range c {
			fmt.Print("\n")
			os.Exit(-1)
		}
	}()
}
<|endoftext|>


<commit_before>package analyze

type splashbackRadiusParams struct { dLim float64 }
type internalSplashbackRadiusOption func(*splashbackRadiusParams)
type SplashbackRadiusOption internalSplashbackRadiusOption

// DLim sets limit for d ln(rho) / d ln(r) above which point cannot be the
// splashback radius. The default value is -5.
func DLim(dLim float64) SplashbackRadiusOption {
	return func(p *splashbackRadiusParams) { p.dLim = dLim }
}

func (p *splashbackRadiusParams) loadOptions(opts []SplashbackRadiusOption) {
	p.dLim = -5
	for _, opt := range opts { opt(p) }
}

func SplashbackRadius(
	rs, rhos, derivs []float64, opts ...SplashbackRadiusOption,
) (r float64, ok bool) {
	p := new(splashbackRadiusParams)
	p.loadOptions(opts)

	if len(rhos) != len(derivs) { panic("len(rhos) != len(derivs)") }
	if len(rhos) == 0 { return 0, false }

	rhoMin := rhos[0]
	dMin, iMin := p.dLim, -1
	for i := 1; i < len(rs) - 1; i++ {
		if rhos[i] < rhoMin {
			rhoMin = rhos[i]
			if isMinimum(derivs, i) && (derivs[i] < dMin) {
				dMin, iMin = derivs[i], i
			}
		}
	}

	if iMin == -1 { return 0, false }
	return rs[iMin], true
}

func isMinimum(xs []float64, i int) bool {
	return xs[i] > xs[i+1] && xs[i] < xs[i-1]
}
<commit_msg>Fixed bug in isMinimum().<commit_after>package analyze

type splashbackRadiusParams struct { dLim float64 }
type internalSplashbackRadiusOption func(*splashbackRadiusParams)
type SplashbackRadiusOption internalSplashbackRadiusOption

// DLim sets limit for d ln(rho) / d ln(r) above which point cannot be the
// splashback radius. The default value is -5.
func DLim(dLim float64) SplashbackRadiusOption {
	return func(p *splashbackRadiusParams) { p.dLim = dLim }
}

func (p *splashbackRadiusParams) loadOptions(opts []SplashbackRadiusOption) {
	p.dLim = -5
	for _, opt := range opts { opt(p) }
}

func SplashbackRadius(
	rs, rhos, derivs []float64, opts ...SplashbackRadiusOption,
) (r float64, ok bool) {
	p := new(splashbackRadiusParams)
	p.loadOptions(opts)

	if len(rhos) != len(derivs) { panic("len(rhos) != len(derivs)") }
	if len(rhos) == 0 { return 0, false }

	rhoMin := rhos[0]
	dMin, iMin := p.dLim, -1
	for i := 1; i < len(rs) - 1; i++ {
		if rhos[i] < rhoMin {
			rhoMin = rhos[i]
			if isMinimum(derivs, i) && (derivs[i] < dMin) {
				dMin, iMin = derivs[i], i
			}
		}
	}

	if iMin == -1 { return 0, false }
	return rs[iMin], true
}

func isMinimum(xs []float64, i int) bool {
	return xs[i] < xs[i+1] && xs[i] < xs[i-1]
}
<|endoftext|>


<commit_before>// Copyright 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 xds

import (
	"fmt"
	"strings"

	discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"

	credscontroller "istio.io/istio/pilot/pkg/credentials"
	"istio.io/istio/pilot/pkg/features"
	"istio.io/istio/pilot/pkg/model"
	"istio.io/istio/pilot/pkg/model/credentials"
	"istio.io/istio/pilot/pkg/networking/util"
	"istio.io/istio/pkg/cluster"
	"istio.io/istio/pkg/config/schema/gvk"
	"istio.io/istio/pkg/util/sets"
)

// EcdsGenerator generates ECDS configuration.
type EcdsGenerator struct {
	Server           *DiscoveryServer
	secretController credscontroller.MulticlusterController
}

var _ model.XdsResourceGenerator = &EcdsGenerator{}

func ecdsNeedsPush(req *model.PushRequest) bool {
	if req == nil {
		return true
	}
	// If none set, we will always push
	if len(req.ConfigsUpdated) == 0 {
		return true
	}
	// Only push if config updates is triggered by EnvoyFilter, WasmPlugin, or Secret.
	for config := range req.ConfigsUpdated {
		switch config.Kind {
		case gvk.EnvoyFilter:
			return true
		case gvk.WasmPlugin:
			return true
		case gvk.Secret:
			return true
		}
	}
	return false
}

// Generate returns ECDS resources for a given proxy.
func (e *EcdsGenerator) Generate(proxy *model.Proxy, w *model.WatchedResource, req *model.PushRequest) (model.Resources, model.XdsLogDetails, error) {
	if !ecdsNeedsPush(req) {
		return nil, model.DefaultXdsLogDetails, nil
	}

	secretResources := referencedSecrets(proxy, req.Push, w.ResourceNames)
	var updatedSecrets map[model.ConfigKey]struct{}
	// Check if the secret updates is relevant to Wasm image pull. If not relevant, skip pushing ECDS.
	if !model.ConfigsHaveKind(req.ConfigsUpdated, gvk.WasmPlugin) && !model.ConfigsHaveKind(req.ConfigsUpdated, gvk.EnvoyFilter) &&
		model.ConfigsHaveKind(req.ConfigsUpdated, gvk.Secret) {
		// Get the updated secrets
		updatedSecrets = model.ConfigsOfKind(req.ConfigsUpdated, gvk.Secret)
		needsPush := false
		for _, sr := range secretResources {
			if _, found := updatedSecrets[model.ConfigKey{Kind: gvk.Secret, Name: sr.Name, Namespace: sr.Namespace}]; found {
				needsPush = true
				break
			}
		}
		if !needsPush {
			return nil, model.DefaultXdsLogDetails, nil
		}
	}

	var secrets map[string][]byte
	if len(secretResources) > 0 {
		// Generate the pull secrets first, which will be used when populating the extension config.
		var secretController credscontroller.Controller
		if e.secretController != nil {
			var err error
			secretController, err = e.secretController.ForCluster(proxy.Metadata.ClusterID)
			if err != nil {
				log.Warnf("proxy %s is from an unknown cluster, cannot retrieve certificates for Wasm image pull: %v", proxy.ID, err)
				return nil, model.DefaultXdsLogDetails, nil
			}
		}
		// Inserts Wasm pull secrets in ECDS response, which will be used at xds proxy for image pull.
		// Before forwarding to Envoy, xds proxy will remove the secret from ECDS response.
		secrets = e.GeneratePullSecrets(proxy, updatedSecrets, secretResources, secretController, req)
	}

	ec := e.Server.ConfigGenerator.BuildExtensionConfiguration(proxy, req.Push, w.ResourceNames, secrets)

	if ec == nil {
		return nil, model.DefaultXdsLogDetails, nil
	}

	resources := make(model.Resources, 0, len(ec))
	for _, c := range ec {
		resources = append(resources, &discovery.Resource{
			Name:     c.Name,
			Resource: util.MessageToAny(c),
		})
	}

	return resources, model.DefaultXdsLogDetails, nil
}

func (e *EcdsGenerator) GeneratePullSecrets(proxy *model.Proxy, updatedSecrets map[model.ConfigKey]struct{}, secretResources []SecretResource,
	secretController credscontroller.Controller, req *model.PushRequest,
) map[string][]byte {
	if features.XDSAuth && features.EnableXDSIdentityCheck && proxy.VerifiedIdentity == nil {
		log.Warnf("proxy %s is not authorized to receive secret. Ensure you are connecting over TLS port and are authenticated.", proxy.ID)
		return nil
	}

	results := make(map[string][]byte)
	for _, sr := range secretResources {
		if updatedSecrets != nil {
			if _, found := updatedSecrets[model.ConfigKey{Kind: gvk.Secret, Name: sr.Name, Namespace: sr.Namespace}]; !found {
				// This is an incremental update, filter out credscontroller that are not updated.
				continue
			}
		}

		cred, err := secretController.GetDockerCredential(sr.Name, sr.Namespace)
		if err != nil {
			log.Warnf("Failed to fetch docker credential %s: %v", sr.ResourceName, err)
		} else {
			results[sr.ResourceName] = cred
		}
	}
	return results
}

func (e *EcdsGenerator) SetCredController(creds credscontroller.MulticlusterController) {
	e.secretController = creds
}

func referencedSecrets(proxy *model.Proxy, push *model.PushContext, resourceNames []string) []SecretResource {
	// The requirement for the Wasm pull secret:
	// * Wasm pull secrets must be of type `kubernetes.io/dockerconfigjson`.
	// * Secret are referenced by a WasmPlugin which applies to this proxy.
	// TODO: we get the WasmPlugins here to get the secrets reference in order to decide whether ECDS push is needed,
	//       and we will get it again at extension config build. Avoid getting it twice if this becomes a problem.
	watched := sets.New(resourceNames...)
	wasmPlugins := push.WasmPlugins(proxy)
	referencedSecrets := sets.Set{}
	for _, wps := range wasmPlugins {
		for _, wp := range wps {
			if watched.Contains(wp.ResourceName) && wp.ImagePullSecret != "" {
				referencedSecrets.Insert(wp.ImagePullSecret)
			}
		}
	}
	var filtered []SecretResource
	for rn := range referencedSecrets {
		sr, err := parseSecretName(rn, proxy.Metadata.ClusterID)
		if err != nil {
			log.Warnf("Failed to parse secret resource name %v: %v", rn, err)
			continue
		}
		filtered = append(filtered, sr)
	}

	return filtered
}

// parseSecretName parses secret resource name from WasmPlugin env variable.
// See toSecretResourceName at model/extensions.go about how secret resource name is generated.
func parseSecretName(resourceName string, proxyCluster cluster.ID) (SecretResource, error) {
	// The secret resource name must be formatted as kubernetes://secret-namespace/secret-name.
	if !strings.HasPrefix(resourceName, credentials.KubernetesSecretTypeURI) {
		return SecretResource{}, fmt.Errorf("misformed Wasm pull secret resource name %v", resourceName)
	}
	res := strings.TrimPrefix(resourceName, credentials.KubernetesSecretTypeURI)
	sep := "/"
	split := strings.Split(res, sep)
	if len(split) != 2 {
		return SecretResource{}, fmt.Errorf("misformed Wasm pull secret resource name %v", resourceName)
	}
	return SecretResource{
		SecretResource: credentials.SecretResource{
			Type:         credentials.KubernetesSecretType,
			Name:         split[1],
			Namespace:    split[0],
			ResourceName: resourceName,
			Cluster:      proxyCluster,
		},
	}, nil
}
<commit_msg>Revert "only check for verified identify if xds auth is enabled (#39370)" (#39374)<commit_after>// Copyright 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 xds

import (
	"fmt"
	"strings"

	discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"

	credscontroller "istio.io/istio/pilot/pkg/credentials"
	"istio.io/istio/pilot/pkg/model"
	"istio.io/istio/pilot/pkg/model/credentials"
	"istio.io/istio/pilot/pkg/networking/util"
	"istio.io/istio/pkg/cluster"
	"istio.io/istio/pkg/config/schema/gvk"
	"istio.io/istio/pkg/util/sets"
)

// EcdsGenerator generates ECDS configuration.
type EcdsGenerator struct {
	Server           *DiscoveryServer
	secretController credscontroller.MulticlusterController
}

var _ model.XdsResourceGenerator = &EcdsGenerator{}

func ecdsNeedsPush(req *model.PushRequest) bool {
	if req == nil {
		return true
	}
	// If none set, we will always push
	if len(req.ConfigsUpdated) == 0 {
		return true
	}
	// Only push if config updates is triggered by EnvoyFilter, WasmPlugin, or Secret.
	for config := range req.ConfigsUpdated {
		switch config.Kind {
		case gvk.EnvoyFilter:
			return true
		case gvk.WasmPlugin:
			return true
		case gvk.Secret:
			return true
		}
	}
	return false
}

// Generate returns ECDS resources for a given proxy.
func (e *EcdsGenerator) Generate(proxy *model.Proxy, w *model.WatchedResource, req *model.PushRequest) (model.Resources, model.XdsLogDetails, error) {
	if !ecdsNeedsPush(req) {
		return nil, model.DefaultXdsLogDetails, nil
	}

	secretResources := referencedSecrets(proxy, req.Push, w.ResourceNames)
	var updatedSecrets map[model.ConfigKey]struct{}
	// Check if the secret updates is relevant to Wasm image pull. If not relevant, skip pushing ECDS.
	if !model.ConfigsHaveKind(req.ConfigsUpdated, gvk.WasmPlugin) && !model.ConfigsHaveKind(req.ConfigsUpdated, gvk.EnvoyFilter) &&
		model.ConfigsHaveKind(req.ConfigsUpdated, gvk.Secret) {
		// Get the updated secrets
		updatedSecrets = model.ConfigsOfKind(req.ConfigsUpdated, gvk.Secret)
		needsPush := false
		for _, sr := range secretResources {
			if _, found := updatedSecrets[model.ConfigKey{Kind: gvk.Secret, Name: sr.Name, Namespace: sr.Namespace}]; found {
				needsPush = true
				break
			}
		}
		if !needsPush {
			return nil, model.DefaultXdsLogDetails, nil
		}
	}

	var secrets map[string][]byte
	if len(secretResources) > 0 {
		// Generate the pull secrets first, which will be used when populating the extension config.
		var secretController credscontroller.Controller
		if e.secretController != nil {
			var err error
			secretController, err = e.secretController.ForCluster(proxy.Metadata.ClusterID)
			if err != nil {
				log.Warnf("proxy %s is from an unknown cluster, cannot retrieve certificates for Wasm image pull: %v", proxy.ID, err)
				return nil, model.DefaultXdsLogDetails, nil
			}
		}
		// Inserts Wasm pull secrets in ECDS response, which will be used at xds proxy for image pull.
		// Before forwarding to Envoy, xds proxy will remove the secret from ECDS response.
		secrets = e.GeneratePullSecrets(proxy, updatedSecrets, secretResources, secretController, req)
	}

	ec := e.Server.ConfigGenerator.BuildExtensionConfiguration(proxy, req.Push, w.ResourceNames, secrets)

	if ec == nil {
		return nil, model.DefaultXdsLogDetails, nil
	}

	resources := make(model.Resources, 0, len(ec))
	for _, c := range ec {
		resources = append(resources, &discovery.Resource{
			Name:     c.Name,
			Resource: util.MessageToAny(c),
		})
	}

	return resources, model.DefaultXdsLogDetails, nil
}

func (e *EcdsGenerator) GeneratePullSecrets(proxy *model.Proxy, updatedSecrets map[model.ConfigKey]struct{}, secretResources []SecretResource,
	secretController credscontroller.Controller, req *model.PushRequest,
) map[string][]byte {
	if proxy.VerifiedIdentity == nil {
		log.Warnf("proxy %s is not authorized to receive secret. Ensure you are connecting over TLS port and are authenticated.", proxy.ID)
		return nil
	}

	results := make(map[string][]byte)
	for _, sr := range secretResources {
		if updatedSecrets != nil {
			if _, found := updatedSecrets[model.ConfigKey{Kind: gvk.Secret, Name: sr.Name, Namespace: sr.Namespace}]; !found {
				// This is an incremental update, filter out credscontroller that are not updated.
				continue
			}
		}

		cred, err := secretController.GetDockerCredential(sr.Name, sr.Namespace)
		if err != nil {
			log.Warnf("Failed to fetch docker credential %s: %v", sr.ResourceName, err)
		} else {
			results[sr.ResourceName] = cred
		}
	}
	return results
}

func (e *EcdsGenerator) SetCredController(creds credscontroller.MulticlusterController) {
	e.secretController = creds
}

func referencedSecrets(proxy *model.Proxy, push *model.PushContext, resourceNames []string) []SecretResource {
	// The requirement for the Wasm pull secret:
	// * Wasm pull secrets must be of type `kubernetes.io/dockerconfigjson`.
	// * Secret are referenced by a WasmPlugin which applies to this proxy.
	// TODO: we get the WasmPlugins here to get the secrets reference in order to decide whether ECDS push is needed,
	//       and we will get it again at extension config build. Avoid getting it twice if this becomes a problem.
	watched := sets.New(resourceNames...)
	wasmPlugins := push.WasmPlugins(proxy)
	referencedSecrets := sets.Set{}
	for _, wps := range wasmPlugins {
		for _, wp := range wps {
			if watched.Contains(wp.ResourceName) && wp.ImagePullSecret != "" {
				referencedSecrets.Insert(wp.ImagePullSecret)
			}
		}
	}
	var filtered []SecretResource
	for rn := range referencedSecrets {
		sr, err := parseSecretName(rn, proxy.Metadata.ClusterID)
		if err != nil {
			log.Warnf("Failed to parse secret resource name %v: %v", rn, err)
			continue
		}
		filtered = append(filtered, sr)
	}

	return filtered
}

// parseSecretName parses secret resource name from WasmPlugin env variable.
// See toSecretResourceName at model/extensions.go about how secret resource name is generated.
func parseSecretName(resourceName string, proxyCluster cluster.ID) (SecretResource, error) {
	// The secret resource name must be formatted as kubernetes://secret-namespace/secret-name.
	if !strings.HasPrefix(resourceName, credentials.KubernetesSecretTypeURI) {
		return SecretResource{}, fmt.Errorf("misformed Wasm pull secret resource name %v", resourceName)
	}
	res := strings.TrimPrefix(resourceName, credentials.KubernetesSecretTypeURI)
	sep := "/"
	split := strings.Split(res, sep)
	if len(split) != 2 {
		return SecretResource{}, fmt.Errorf("misformed Wasm pull secret resource name %v", resourceName)
	}
	return SecretResource{
		SecretResource: credentials.SecretResource{
			Type:         credentials.KubernetesSecretType,
			Name:         split[1],
			Namespace:    split[0],
			ResourceName: resourceName,
			Cluster:      proxyCluster,
		},
	}, nil
}
<|endoftext|>