<commit_before>/*
Copyright 2017 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 server

import (
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httptest"
	"net/http/httputil"
	"reflect"
	"testing"
	"time"

	"github.com/google/go-cmp/cmp"
	"k8s.io/apimachinery/pkg/util/json"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/apimachinery/pkg/util/waitgroup"
	auditinternal "k8s.io/apiserver/pkg/apis/audit"
	"k8s.io/apiserver/pkg/audit"
	"k8s.io/apiserver/pkg/audit/policy"
	"k8s.io/apiserver/pkg/authentication/authenticator"
	"k8s.io/apiserver/pkg/authentication/user"
	"k8s.io/apiserver/pkg/endpoints/request"
	"k8s.io/apiserver/pkg/server/healthz"
	"k8s.io/client-go/informers"
	"k8s.io/client-go/kubernetes/fake"
	"k8s.io/client-go/rest"
)

func TestAuthorizeClientBearerTokenNoops(t *testing.T) {
	// All of these should do nothing (not panic, no side-effects)
	cfgGens := []func() *rest.Config{
		func() *rest.Config { return nil },
		func() *rest.Config { return &rest.Config{} },
		func() *rest.Config { return &rest.Config{BearerToken: "mu"} },
	}
	authcGens := []func() *AuthenticationInfo{
		func() *AuthenticationInfo { return nil },
		func() *AuthenticationInfo { return &AuthenticationInfo{} },
	}
	authzGens := []func() *AuthorizationInfo{
		func() *AuthorizationInfo { return nil },
		func() *AuthorizationInfo { return &AuthorizationInfo{} },
	}
	for _, cfgGen := range cfgGens {
		for _, authcGen := range authcGens {
			for _, authzGen := range authzGens {
				pConfig := cfgGen()
				pAuthc := authcGen()
				pAuthz := authzGen()
				AuthorizeClientBearerToken(pConfig, pAuthc, pAuthz)
				if before, after := authcGen(), pAuthc; !reflect.DeepEqual(before, after) {
					t.Errorf("AuthorizeClientBearerToken(%v, %#+v, %v) changed %#+v", pConfig, pAuthc, pAuthz, *before)
				}
				if before, after := authzGen(), pAuthz; !reflect.DeepEqual(before, after) {
					t.Errorf("AuthorizeClientBearerToken(%v, %v, %#+v) changed %#+v", pConfig, pAuthc, pAuthz, *before)
				}
			}
		}
	}
}

func TestNewWithDelegate(t *testing.T) {
	delegateConfig := NewConfig(codecs)
	delegateConfig.ExternalAddress = "192.168.10.4:443"
	delegateConfig.PublicAddress = net.ParseIP("192.168.10.4")
	delegateConfig.LegacyAPIGroupPrefixes = sets.NewString("/api")
	delegateConfig.LoopbackClientConfig = &rest.Config{}
	clientset := fake.NewSimpleClientset()
	if clientset == nil {
		t.Fatal("unable to create fake client set")
	}

	delegateConfig.HealthzChecks = append(delegateConfig.HealthzChecks, healthz.NamedCheck("delegate-health", func(r *http.Request) error {
		return fmt.Errorf("delegate failed healthcheck")
	}))

	sharedInformers := informers.NewSharedInformerFactory(clientset, delegateConfig.LoopbackClientConfig.Timeout)
	delegateServer, err := delegateConfig.Complete(sharedInformers).New("test", NewEmptyDelegate())
	if err != nil {
		t.Fatal(err)
	}
	delegateServer.Handler.NonGoRestfulMux.HandleFunc("/foo", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusForbidden)
	})

	delegatePostStartHookChan := make(chan struct{})
	delegateServer.AddPostStartHookOrDie("delegate-post-start-hook", func(context PostStartHookContext) error {
		defer close(delegatePostStartHookChan)
		return nil
	})

	// this wires up swagger
	delegateServer.PrepareRun()

	wrappingConfig := NewConfig(codecs)
	wrappingConfig.ExternalAddress = "192.168.10.4:443"
	wrappingConfig.PublicAddress = net.ParseIP("192.168.10.4")
	wrappingConfig.LegacyAPIGroupPrefixes = sets.NewString("/api")
	wrappingConfig.LoopbackClientConfig = &rest.Config{}

	wrappingConfig.HealthzChecks = append(wrappingConfig.HealthzChecks, healthz.NamedCheck("wrapping-health", func(r *http.Request) error {
		return fmt.Errorf("wrapping failed healthcheck")
	}))

	sharedInformers = informers.NewSharedInformerFactory(clientset, wrappingConfig.LoopbackClientConfig.Timeout)
	wrappingServer, err := wrappingConfig.Complete(sharedInformers).New("test", delegateServer)
	if err != nil {
		t.Fatal(err)
	}
	wrappingServer.Handler.NonGoRestfulMux.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusUnauthorized)
	})

	wrappingPostStartHookChan := make(chan struct{})
	wrappingServer.AddPostStartHookOrDie("wrapping-post-start-hook", func(context PostStartHookContext) error {
		defer close(wrappingPostStartHookChan)
		return nil
	})

	stopCh := make(chan struct{})
	defer close(stopCh)
	wrappingServer.PrepareRun()
	wrappingServer.RunPostStartHooks(stopCh)

	server := httptest.NewServer(wrappingServer.Handler)
	defer server.Close()

	// Wait for the hooks to finish before checking the response
	<-delegatePostStartHookChan
	<-wrappingPostStartHookChan
	expectedPaths := []string{
		"/apis",
		"/bar",
		"/foo",
		"/healthz",
		"/healthz/delegate-health",
		"/healthz/log",
		"/healthz/ping",
		"/healthz/poststarthook/delegate-post-start-hook",
		"/healthz/poststarthook/generic-apiserver-start-informers",
		"/healthz/poststarthook/max-in-flight-filter",
		"/healthz/poststarthook/wrapping-post-start-hook",
		"/healthz/wrapping-health",
		"/livez",
		"/livez/delegate-health",
		"/livez/log",
		"/livez/ping",
		"/livez/poststarthook/delegate-post-start-hook",
		"/livez/poststarthook/generic-apiserver-start-informers",
		"/livez/poststarthook/max-in-flight-filter",
		"/livez/poststarthook/wrapping-post-start-hook",
		"/metrics",
		"/readyz",
		"/readyz/delegate-health",
		"/readyz/informer-sync",
		"/readyz/log",
		"/readyz/ping",
		"/readyz/poststarthook/delegate-post-start-hook",
		"/readyz/poststarthook/generic-apiserver-start-informers",
		"/readyz/poststarthook/max-in-flight-filter",
		"/readyz/poststarthook/wrapping-post-start-hook",
		"/readyz/shutdown",
	}
	checkExpectedPathsAtRoot(server.URL, expectedPaths, t)
	checkPath(server.URL+"/healthz", http.StatusInternalServerError, `[+]ping ok
[+]log ok
[-]wrapping-health failed: reason withheld
[-]delegate-health failed: reason withheld
[+]poststarthook/generic-apiserver-start-informers ok
[+]poststarthook/max-in-flight-filter ok
[+]poststarthook/delegate-post-start-hook ok
[+]poststarthook/wrapping-post-start-hook ok
healthz check failed
`, t)

	checkPath(server.URL+"/healthz/delegate-health", http.StatusInternalServerError, `internal server error: delegate failed healthcheck
`, t)
	checkPath(server.URL+"/healthz/wrapping-health", http.StatusInternalServerError, `internal server error: wrapping failed healthcheck
`, t)
	checkPath(server.URL+"/healthz/poststarthook/delegate-post-start-hook", http.StatusOK, `ok`, t)
	checkPath(server.URL+"/healthz/poststarthook/wrapping-post-start-hook", http.StatusOK, `ok`, t)
	checkPath(server.URL+"/foo", http.StatusForbidden, ``, t)
	checkPath(server.URL+"/bar", http.StatusUnauthorized, ``, t)
}

func checkPath(url string, expectedStatusCode int, expectedBody string, t *testing.T) {
	t.Run(url, func(t *testing.T) {
		resp, err := http.Get(url)
		if err != nil {
			t.Fatal(err)
		}
		dump, _ := httputil.DumpResponse(resp, true)
		t.Log(string(dump))

		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			t.Fatal(err)
		}

		if e, a := expectedBody, string(body); e != a {
			t.Errorf("%q expected %v, got %v", url, e, a)
		}
		if e, a := expectedStatusCode, resp.StatusCode; e != a {
			t.Errorf("%q expected %v, got %v", url, e, a)
		}
	})
}

func checkExpectedPathsAtRoot(url string, expectedPaths []string, t *testing.T) {
	t.Run(url, func(t *testing.T) {
		resp, err := http.Get(url)
		if err != nil {
			t.Fatal(err)
		}
		dump, _ := httputil.DumpResponse(resp, true)
		t.Log(string(dump))

		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			t.Fatal(err)
		}
		var result map[string]interface{}
		json.Unmarshal(body, &result)
		paths, ok := result["paths"].([]interface{})
		if !ok {
			t.Errorf("paths not found")
		}
		pathset := sets.NewString()
		for _, p := range paths {
			pathset.Insert(p.(string))
		}
		expectedset := sets.NewString(expectedPaths...)
		for p := range pathset.Difference(expectedset) {
			t.Errorf("Got %v path, which we did not expect", p)
		}
		for p := range expectedset.Difference(pathset) {
			t.Errorf(" Expected %v path which we did not get", p)
		}
	})
}

func TestAuthenticationAuditAnnotationsDefaultChain(t *testing.T) {
	authn := authenticator.RequestFunc(func(req *http.Request) (*authenticator.Response, bool, error) {
		// confirm that we can set an audit annotation in a handler before WithAudit
		audit.AddAuditAnnotation(req.Context(), "pandas", "are awesome")

		// confirm that trying to use the audit event directly would never work
		if ae := request.AuditEventFrom(req.Context()); ae != nil {
			t.Errorf("expected nil audit event, got %v", ae)
		}

		return &authenticator.Response{User: &user.DefaultInfo{}}, true, nil
	})
	backend := &testBackend{}
	c := &Config{
		Authentication:     AuthenticationInfo{Authenticator: authn},
		AuditBackend:       backend,
		AuditPolicyChecker: policy.FakeChecker(auditinternal.LevelMetadata, nil),

		// avoid nil panics
		HandlerChainWaitGroup: &waitgroup.SafeWaitGroup{},
		RequestInfoResolver:   &request.RequestInfoFactory{},
		RequestTimeout:        10 * time.Second,
		LongRunningFunc:       func(_ *http.Request, _ *request.RequestInfo) bool { return false },
	}

	h := DefaultBuildHandlerChain(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// confirm this is a no-op
		if r.Context() != audit.WithAuditAnnotations(r.Context()) {
			t.Error("unexpected double wrapping of context")
		}

		// confirm that we have an audit event
		ae := request.AuditEventFrom(r.Context())
		if ae == nil {
			t.Error("unexpected nil audit event")
		}

		// confirm that the direct way of setting audit annotations later in the chain works as expected
		audit.LogAnnotation(ae, "snorlax", "is cool too")

		// confirm that the indirect way of setting audit annotations later in the chain also works
		audit.AddAuditAnnotation(r.Context(), "dogs", "are okay")

		if _, err := w.Write([]byte("done")); err != nil {
			t.Errorf("failed to write response: %v", err)
		}
	}), c)
	w := httptest.NewRecorder()

	h.ServeHTTP(w, httptest.NewRequest("GET", "https://ignored.com", nil))

	r := w.Result()
	if ok := r.StatusCode == http.StatusOK && w.Body.String() == "done" && len(r.Header.Get(auditinternal.HeaderAuditID)) > 0; !ok {
		t.Errorf("invalid response: %#v", w)
	}
	if len(backend.events) == 0 {
		t.Error("expected audit events, got none")
	}
	// these should all be the same because the handler chain mutates the event in place
	want := map[string]string{"pandas": "are awesome", "snorlax": "is cool too", "dogs": "are okay"}
	for _, event := range backend.events {
		if event.Stage != auditinternal.StageResponseComplete {
			t.Errorf("expected event stage to be complete, got: %s", event.Stage)
		}
		if diff := cmp.Diff(want, event.Annotations); diff != "" {
			t.Errorf("event has unexpected annotations (-want +got): %s", diff)
		}
	}
}

type testBackend struct {
	events []*auditinternal.Event

	audit.Backend // nil panic if anything other than ProcessEvents called
}

func (b *testBackend) ProcessEvents(events ...*auditinternal.Event) bool {
	b.events = append(b.events, events...)
	return true
}
<commit_msg>wait for poststarthook/max-in-flight-filter to be initialized asynchronously<commit_after>/*
Copyright 2017 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 server

import (
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httptest"
	"net/http/httputil"
	"reflect"
	"testing"
	"time"

	"github.com/google/go-cmp/cmp"
	"k8s.io/apimachinery/pkg/util/json"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/apimachinery/pkg/util/waitgroup"
	auditinternal "k8s.io/apiserver/pkg/apis/audit"
	"k8s.io/apiserver/pkg/audit"
	"k8s.io/apiserver/pkg/audit/policy"
	"k8s.io/apiserver/pkg/authentication/authenticator"
	"k8s.io/apiserver/pkg/authentication/user"
	"k8s.io/apiserver/pkg/endpoints/request"
	"k8s.io/apiserver/pkg/server/healthz"
	"k8s.io/client-go/informers"
	"k8s.io/client-go/kubernetes/fake"
	"k8s.io/client-go/rest"
)

func TestAuthorizeClientBearerTokenNoops(t *testing.T) {
	// All of these should do nothing (not panic, no side-effects)
	cfgGens := []func() *rest.Config{
		func() *rest.Config { return nil },
		func() *rest.Config { return &rest.Config{} },
		func() *rest.Config { return &rest.Config{BearerToken: "mu"} },
	}
	authcGens := []func() *AuthenticationInfo{
		func() *AuthenticationInfo { return nil },
		func() *AuthenticationInfo { return &AuthenticationInfo{} },
	}
	authzGens := []func() *AuthorizationInfo{
		func() *AuthorizationInfo { return nil },
		func() *AuthorizationInfo { return &AuthorizationInfo{} },
	}
	for _, cfgGen := range cfgGens {
		for _, authcGen := range authcGens {
			for _, authzGen := range authzGens {
				pConfig := cfgGen()
				pAuthc := authcGen()
				pAuthz := authzGen()
				AuthorizeClientBearerToken(pConfig, pAuthc, pAuthz)
				if before, after := authcGen(), pAuthc; !reflect.DeepEqual(before, after) {
					t.Errorf("AuthorizeClientBearerToken(%v, %#+v, %v) changed %#+v", pConfig, pAuthc, pAuthz, *before)
				}
				if before, after := authzGen(), pAuthz; !reflect.DeepEqual(before, after) {
					t.Errorf("AuthorizeClientBearerToken(%v, %v, %#+v) changed %#+v", pConfig, pAuthc, pAuthz, *before)
				}
			}
		}
	}
}

func TestNewWithDelegate(t *testing.T) {
	delegateConfig := NewConfig(codecs)
	delegateConfig.ExternalAddress = "192.168.10.4:443"
	delegateConfig.PublicAddress = net.ParseIP("192.168.10.4")
	delegateConfig.LegacyAPIGroupPrefixes = sets.NewString("/api")
	delegateConfig.LoopbackClientConfig = &rest.Config{}
	clientset := fake.NewSimpleClientset()
	if clientset == nil {
		t.Fatal("unable to create fake client set")
	}

	delegateConfig.HealthzChecks = append(delegateConfig.HealthzChecks, healthz.NamedCheck("delegate-health", func(r *http.Request) error {
		return fmt.Errorf("delegate failed healthcheck")
	}))

	sharedInformers := informers.NewSharedInformerFactory(clientset, delegateConfig.LoopbackClientConfig.Timeout)
	delegateServer, err := delegateConfig.Complete(sharedInformers).New("test", NewEmptyDelegate())
	if err != nil {
		t.Fatal(err)
	}
	delegateServer.Handler.NonGoRestfulMux.HandleFunc("/foo", func(w http.ResponseWriter, _ *http.Request) {
		w.WriteHeader(http.StatusForbidden)
	})

	delegatePostStartHookChan := make(chan struct{})
	delegateServer.AddPostStartHookOrDie("delegate-post-start-hook", func(context PostStartHookContext) error {
		defer close(delegatePostStartHookChan)
		return nil
	})

	// this wires up swagger
	delegateServer.PrepareRun()

	wrappingConfig := NewConfig(codecs)
	wrappingConfig.ExternalAddress = "192.168.10.4:443"
	wrappingConfig.PublicAddress = net.ParseIP("192.168.10.4")
	wrappingConfig.LegacyAPIGroupPrefixes = sets.NewString("/api")
	wrappingConfig.LoopbackClientConfig = &rest.Config{}

	wrappingConfig.HealthzChecks = append(wrappingConfig.HealthzChecks, healthz.NamedCheck("wrapping-health", func(r *http.Request) error {
		return fmt.Errorf("wrapping failed healthcheck")
	}))

	sharedInformers = informers.NewSharedInformerFactory(clientset, wrappingConfig.LoopbackClientConfig.Timeout)
	wrappingServer, err := wrappingConfig.Complete(sharedInformers).New("test", delegateServer)
	if err != nil {
		t.Fatal(err)
	}
	wrappingServer.Handler.NonGoRestfulMux.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusUnauthorized)
	})

	wrappingPostStartHookChan := make(chan struct{})
	wrappingServer.AddPostStartHookOrDie("wrapping-post-start-hook", func(context PostStartHookContext) error {
		defer close(wrappingPostStartHookChan)
		return nil
	})

	stopCh := make(chan struct{})
	defer close(stopCh)
	wrappingServer.PrepareRun()
	wrappingServer.RunPostStartHooks(stopCh)

	server := httptest.NewServer(wrappingServer.Handler)
	defer server.Close()

	// Wait for the hooks to finish before checking the response
	<-delegatePostStartHookChan
	<-wrappingPostStartHookChan
	expectedPaths := []string{
		"/apis",
		"/bar",
		"/foo",
		"/healthz",
		"/healthz/delegate-health",
		"/healthz/log",
		"/healthz/ping",
		"/healthz/poststarthook/delegate-post-start-hook",
		"/healthz/poststarthook/generic-apiserver-start-informers",
		"/healthz/poststarthook/max-in-flight-filter",
		"/healthz/poststarthook/wrapping-post-start-hook",
		"/healthz/wrapping-health",
		"/livez",
		"/livez/delegate-health",
		"/livez/log",
		"/livez/ping",
		"/livez/poststarthook/delegate-post-start-hook",
		"/livez/poststarthook/generic-apiserver-start-informers",
		"/livez/poststarthook/max-in-flight-filter",
		"/livez/poststarthook/wrapping-post-start-hook",
		"/metrics",
		"/readyz",
		"/readyz/delegate-health",
		"/readyz/informer-sync",
		"/readyz/log",
		"/readyz/ping",
		"/readyz/poststarthook/delegate-post-start-hook",
		"/readyz/poststarthook/generic-apiserver-start-informers",
		"/readyz/poststarthook/max-in-flight-filter",
		"/readyz/poststarthook/wrapping-post-start-hook",
		"/readyz/shutdown",
	}
	checkExpectedPathsAtRoot(server.URL, expectedPaths, t)

	// wait for health (max-in-flight-filter is initialized asynchronously, can take a few milliseconds to initialize)
	if err := wait.PollImmediate(100*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) {
		// healthz checks are installed in PrepareRun
		resp, err := http.Get(server.URL + "/healthz?exclude=wrapping-health&exclude=delegate-health")
		if err != nil {
			t.Fatal(err)
		}
		data, _ := ioutil.ReadAll(resp.Body)
		if http.StatusOK != resp.StatusCode {
			t.Logf("got %d", resp.StatusCode)
			t.Log(string(data))
			return false, nil
		}
		return true, nil
	}); err != nil {
		t.Fatal(err)
	}
	checkPath(server.URL+"/healthz", http.StatusInternalServerError, `[+]ping ok
[+]log ok
[-]wrapping-health failed: reason withheld
[-]delegate-health failed: reason withheld
[+]poststarthook/generic-apiserver-start-informers ok
[+]poststarthook/max-in-flight-filter ok
[+]poststarthook/delegate-post-start-hook ok
[+]poststarthook/wrapping-post-start-hook ok
healthz check failed
`, t)

	checkPath(server.URL+"/healthz/delegate-health", http.StatusInternalServerError, `internal server error: delegate failed healthcheck
`, t)
	checkPath(server.URL+"/healthz/wrapping-health", http.StatusInternalServerError, `internal server error: wrapping failed healthcheck
`, t)
	checkPath(server.URL+"/healthz/poststarthook/delegate-post-start-hook", http.StatusOK, `ok`, t)
	checkPath(server.URL+"/healthz/poststarthook/wrapping-post-start-hook", http.StatusOK, `ok`, t)
	checkPath(server.URL+"/foo", http.StatusForbidden, ``, t)
	checkPath(server.URL+"/bar", http.StatusUnauthorized, ``, t)
}

func checkPath(url string, expectedStatusCode int, expectedBody string, t *testing.T) {
	t.Run(url, func(t *testing.T) {
		resp, err := http.Get(url)
		if err != nil {
			t.Fatal(err)
		}
		dump, _ := httputil.DumpResponse(resp, true)
		t.Log(string(dump))

		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			t.Fatal(err)
		}

		if e, a := expectedBody, string(body); e != a {
			t.Errorf("%q expected %v, got %v", url, e, a)
		}
		if e, a := expectedStatusCode, resp.StatusCode; e != a {
			t.Errorf("%q expected %v, got %v", url, e, a)
		}
	})
}

func checkExpectedPathsAtRoot(url string, expectedPaths []string, t *testing.T) {
	t.Run(url, func(t *testing.T) {
		resp, err := http.Get(url)
		if err != nil {
			t.Fatal(err)
		}
		dump, _ := httputil.DumpResponse(resp, true)
		t.Log(string(dump))

		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			t.Fatal(err)
		}
		var result map[string]interface{}
		json.Unmarshal(body, &result)
		paths, ok := result["paths"].([]interface{})
		if !ok {
			t.Errorf("paths not found")
		}
		pathset := sets.NewString()
		for _, p := range paths {
			pathset.Insert(p.(string))
		}
		expectedset := sets.NewString(expectedPaths...)
		for p := range pathset.Difference(expectedset) {
			t.Errorf("Got %v path, which we did not expect", p)
		}
		for p := range expectedset.Difference(pathset) {
			t.Errorf(" Expected %v path which we did not get", p)
		}
	})
}

func TestAuthenticationAuditAnnotationsDefaultChain(t *testing.T) {
	authn := authenticator.RequestFunc(func(req *http.Request) (*authenticator.Response, bool, error) {
		// confirm that we can set an audit annotation in a handler before WithAudit
		audit.AddAuditAnnotation(req.Context(), "pandas", "are awesome")

		// confirm that trying to use the audit event directly would never work
		if ae := request.AuditEventFrom(req.Context()); ae != nil {
			t.Errorf("expected nil audit event, got %v", ae)
		}

		return &authenticator.Response{User: &user.DefaultInfo{}}, true, nil
	})
	backend := &testBackend{}
	c := &Config{
		Authentication:     AuthenticationInfo{Authenticator: authn},
		AuditBackend:       backend,
		AuditPolicyChecker: policy.FakeChecker(auditinternal.LevelMetadata, nil),

		// avoid nil panics
		HandlerChainWaitGroup: &waitgroup.SafeWaitGroup{},
		RequestInfoResolver:   &request.RequestInfoFactory{},
		RequestTimeout:        10 * time.Second,
		LongRunningFunc:       func(_ *http.Request, _ *request.RequestInfo) bool { return false },
	}

	h := DefaultBuildHandlerChain(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// confirm this is a no-op
		if r.Context() != audit.WithAuditAnnotations(r.Context()) {
			t.Error("unexpected double wrapping of context")
		}

		// confirm that we have an audit event
		ae := request.AuditEventFrom(r.Context())
		if ae == nil {
			t.Error("unexpected nil audit event")
		}

		// confirm that the direct way of setting audit annotations later in the chain works as expected
		audit.LogAnnotation(ae, "snorlax", "is cool too")

		// confirm that the indirect way of setting audit annotations later in the chain also works
		audit.AddAuditAnnotation(r.Context(), "dogs", "are okay")

		if _, err := w.Write([]byte("done")); err != nil {
			t.Errorf("failed to write response: %v", err)
		}
	}), c)
	w := httptest.NewRecorder()

	h.ServeHTTP(w, httptest.NewRequest("GET", "https://ignored.com", nil))

	r := w.Result()
	if ok := r.StatusCode == http.StatusOK && w.Body.String() == "done" && len(r.Header.Get(auditinternal.HeaderAuditID)) > 0; !ok {
		t.Errorf("invalid response: %#v", w)
	}
	if len(backend.events) == 0 {
		t.Error("expected audit events, got none")
	}
	// these should all be the same because the handler chain mutates the event in place
	want := map[string]string{"pandas": "are awesome", "snorlax": "is cool too", "dogs": "are okay"}
	for _, event := range backend.events {
		if event.Stage != auditinternal.StageResponseComplete {
			t.Errorf("expected event stage to be complete, got: %s", event.Stage)
		}
		if diff := cmp.Diff(want, event.Annotations); diff != "" {
			t.Errorf("event has unexpected annotations (-want +got): %s", diff)
		}
	}
}

type testBackend struct {
	events []*auditinternal.Event

	audit.Backend // nil panic if anything other than ProcessEvents called
}

func (b *testBackend) ProcessEvents(events ...*auditinternal.Event) bool {
	b.events = append(b.events, events...)
	return true
}
<|endoftext|>


<commit_before>/*
Copyright 2015 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 storage

import (
	"context"
	"fmt"

	"k8s.io/apimachinery/pkg/api/meta"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/watch"
)

// Versioner abstracts setting and retrieving metadata fields from database response
// onto the object ot list. It is required to maintain storage invariants - updating an
// object twice with the same data except for the ResourceVersion and SelfLink must be
// a no-op. A resourceVersion of type uint64 is a 'raw' resourceVersion,
// intended to be sent directly to or from the backend. A resourceVersion of
// type string is a 'safe' resourceVersion, intended for consumption by users.
type Versioner interface {
	// UpdateObject sets storage metadata into an API object. Returns an error if the object
	// cannot be updated correctly. May return nil if the requested object does not need metadata
	// from database.
	UpdateObject(obj runtime.Object, resourceVersion uint64) error
	// UpdateList sets the resource version into an API list object. Returns an error if the object
	// cannot be updated correctly. May return nil if the requested object does not need metadata
	// from database. continueValue is optional and indicates that more results are available if
	// the client passes that value to the server in a subsequent call.
	UpdateList(obj runtime.Object, resourceVersion uint64, continueValue string) error
	// PrepareObjectForStorage should set SelfLink and ResourceVersion to the empty value. Should
	// return an error if the specified object cannot be updated.
	PrepareObjectForStorage(obj runtime.Object) error
	// ObjectResourceVersion returns the resource version (for persistence) of the specified object.
	// Should return an error if the specified object does not have a persistable version.
	ObjectResourceVersion(obj runtime.Object) (uint64, error)

	// ParseResourceVersion takes a resource version argument and
	// converts it to the storage backend. For watch we should pass to helper.Watch().
	// Because resourceVersion is an opaque value, the default watch
	// behavior for non-zero watch is to watch the next value (if you pass
	// "1", you will see updates from "2" onwards).
	ParseResourceVersion(resourceVersion string) (uint64, error)
}

// ResponseMeta contains information about the database metadata that is associated with
// an object. It abstracts the actual underlying objects to prevent coupling with concrete
// database and to improve testability.
type ResponseMeta struct {
	// TTL is the time to live of the node that contained the returned object. It may be
	// zero or negative in some cases (objects may be expired after the requested
	// expiration time due to server lag).
	TTL int64
	// The resource version of the node that contained the returned object.
	ResourceVersion uint64
}

// MatchValue defines a pair (<index name>, <value for that index>).
type MatchValue struct {
	IndexName string
	Value     string
}

// TriggerPublisherFunc is a function that takes an object, and returns a list of pairs
// (<index name>, <index value for the given object>) for all indexes known
// to that function.
type TriggerPublisherFunc func(obj runtime.Object) []MatchValue

// Everything accepts all objects.
var Everything = SelectionPredicate{
	Label: labels.Everything(),
	Field: fields.Everything(),
}

// Pass an UpdateFunc to Interface.GuaranteedUpdate to make an update
// that is guaranteed to succeed.
// See the comment for GuaranteedUpdate for more details.
type UpdateFunc func(input runtime.Object, res ResponseMeta) (output runtime.Object, ttl *uint64, err error)

// Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.
type Preconditions struct {
	// Specifies the target UID.
	// +optional
	UID *types.UID `json:"uid,omitempty"`
	// Specifies the target ResourceVersion
	// +optional
	ResourceVersion *string `json:"resourceVersion,omitempty"`
}

// NewUIDPreconditions returns a Preconditions with UID set.
func NewUIDPreconditions(uid string) *Preconditions {
	u := types.UID(uid)
	return &Preconditions{UID: &u}
}

func (p *Preconditions) Check(key string, obj runtime.Object) error {
	if p == nil {
		return nil
	}
	objMeta, err := meta.Accessor(obj)
	if err != nil {
		return NewInternalErrorf(
			"can't enforce preconditions %v on un-introspectable object %v, got error: %v",
			*p,
			obj,
			err)
	}
	if p.UID != nil && *p.UID != objMeta.GetUID() {
		err := fmt.Sprintf(
			"Precondition failed: UID in precondition: %v, UID in object meta: %v",
			*p.UID,
			objMeta.GetUID())
		return NewInvalidObjError(key, err)
	}
	return nil

}

// Interface offers a common interface for object marshaling/unmarshaling operations and
// hides all the storage-related operations behind it.
type Interface interface {
	// Returns Versioner associated with this interface.
	Versioner() Versioner

	// Create adds a new object at a key unless it already exists. 'ttl' is time-to-live
	// in seconds (0 means forever). If no error is returned and out is not nil, out will be
	// set to the read value from database.
	Create(ctx context.Context, key string, obj, out runtime.Object, ttl uint64) error

	// Delete removes the specified key and returns the value that existed at that spot.
	// If key didn't exist, it will return NotFound storage error.
	Delete(ctx context.Context, key string, out runtime.Object, preconditions *Preconditions) error

	// Watch begins watching the specified key. Events are decoded into API objects,
	// and any items selected by 'p' are sent down to returned watch.Interface.
	// resourceVersion may be used to specify what version to begin watching,
	// which should be the current resourceVersion, and no longer rv+1
	// (e.g. reconnecting without missing any updates).
	// If resource version is "0", this interface will get current object at given key
	// and send it in an "ADDED" event, before watch starts.
	Watch(ctx context.Context, key string, resourceVersion string, p SelectionPredicate) (watch.Interface, error)

	// WatchList begins watching the specified key's items. Items are decoded into API
	// objects and any item selected by 'p' are sent down to returned watch.Interface.
	// resourceVersion may be used to specify what version to begin watching,
	// which should be the current resourceVersion, and no longer rv+1
	// (e.g. reconnecting without missing any updates).
	// If resource version is "0", this interface will list current objects directory defined by key
	// and send them in "ADDED" events, before watch starts.
	WatchList(ctx context.Context, key string, resourceVersion string, p SelectionPredicate) (watch.Interface, error)

	// Get unmarshals json found at key into objPtr. On a not found error, will either
	// return a zero object of the requested type, or an error, depending on ignoreNotFound.
	// Treats empty responses and nil response nodes exactly like a not found error.
	// The returned contents may be delayed, but it is guaranteed that they will
	// be have at least 'resourceVersion'.
	Get(ctx context.Context, key string, resourceVersion string, objPtr runtime.Object, ignoreNotFound bool) error

	// GetToList unmarshals json found at key and opaque it into *List api object
	// (an object that satisfies the runtime.IsList definition).
	// The returned contents may be delayed, but it is guaranteed that they will
	// be have at least 'resourceVersion'.
	GetToList(ctx context.Context, key string, resourceVersion string, p SelectionPredicate, listObj runtime.Object) error

	// List unmarshalls jsons found at directory defined by key and opaque them
	// into *List api object (an object that satisfies runtime.IsList definition).
	// The returned contents may be delayed, but it is guaranteed that they will
	// be have at least 'resourceVersion'.
	List(ctx context.Context, key string, resourceVersion string, p SelectionPredicate, listObj runtime.Object) error

	// GuaranteedUpdate keeps calling 'tryUpdate()' to update key 'key' (of type 'ptrToType')
	// retrying the update until success if there is index conflict.
	// Note that object passed to tryUpdate may change across invocations of tryUpdate() if
	// other writers are simultaneously updating it, so tryUpdate() needs to take into account
	// the current contents of the object when deciding how the update object should look.
	// If the key doesn't exist, it will return NotFound storage error if ignoreNotFound=false
	// or zero value in 'ptrToType' parameter otherwise.
	// If the object to update has the same value as previous, it won't do any update
	// but will return the object in 'ptrToType' parameter.
	// If 'suggestion' can contain zero or one element - in such case this can be used as
	// a suggestion about the current version of the object to avoid read operation from
	// storage to get it.
	//
	// Example:
	//
	// s := /* implementation of Interface */
	// err := s.GuaranteedUpdate(
	//     "myKey", &MyType{}, true,
	//     func(input runtime.Object, res ResponseMeta) (runtime.Object, *uint64, error) {
	//       // Before each incovation of the user defined function, "input" is reset to
	//       // current contents for "myKey" in database.
	//       curr := input.(*MyType)  // Guaranteed to succeed.
	//
	//       // Make the modification
	//       curr.Counter++
	//
	//       // Return the modified object - return an error to stop iterating. Return
	//       // a uint64 to alter the TTL on the object, or nil to keep it the same value.
	//       return cur, nil, nil
	//    }
	// })
	GuaranteedUpdate(
		ctx context.Context, key string, ptrToType runtime.Object, ignoreNotFound bool,
		precondtions *Preconditions, tryUpdate UpdateFunc, suggestion ...runtime.Object) error

	// Count returns number of different entries under the key (generally being path prefix).
	Count(key string) (int64, error)
}
<commit_msg>add ResourceVersion check to Preconditions#Check<commit_after>/*
Copyright 2015 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 storage

import (
	"context"
	"fmt"

	"k8s.io/apimachinery/pkg/api/meta"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/watch"
)

// Versioner abstracts setting and retrieving metadata fields from database response
// onto the object ot list. It is required to maintain storage invariants - updating an
// object twice with the same data except for the ResourceVersion and SelfLink must be
// a no-op. A resourceVersion of type uint64 is a 'raw' resourceVersion,
// intended to be sent directly to or from the backend. A resourceVersion of
// type string is a 'safe' resourceVersion, intended for consumption by users.
type Versioner interface {
	// UpdateObject sets storage metadata into an API object. Returns an error if the object
	// cannot be updated correctly. May return nil if the requested object does not need metadata
	// from database.
	UpdateObject(obj runtime.Object, resourceVersion uint64) error
	// UpdateList sets the resource version into an API list object. Returns an error if the object
	// cannot be updated correctly. May return nil if the requested object does not need metadata
	// from database. continueValue is optional and indicates that more results are available if
	// the client passes that value to the server in a subsequent call.
	UpdateList(obj runtime.Object, resourceVersion uint64, continueValue string) error
	// PrepareObjectForStorage should set SelfLink and ResourceVersion to the empty value. Should
	// return an error if the specified object cannot be updated.
	PrepareObjectForStorage(obj runtime.Object) error
	// ObjectResourceVersion returns the resource version (for persistence) of the specified object.
	// Should return an error if the specified object does not have a persistable version.
	ObjectResourceVersion(obj runtime.Object) (uint64, error)

	// ParseResourceVersion takes a resource version argument and
	// converts it to the storage backend. For watch we should pass to helper.Watch().
	// Because resourceVersion is an opaque value, the default watch
	// behavior for non-zero watch is to watch the next value (if you pass
	// "1", you will see updates from "2" onwards).
	ParseResourceVersion(resourceVersion string) (uint64, error)
}

// ResponseMeta contains information about the database metadata that is associated with
// an object. It abstracts the actual underlying objects to prevent coupling with concrete
// database and to improve testability.
type ResponseMeta struct {
	// TTL is the time to live of the node that contained the returned object. It may be
	// zero or negative in some cases (objects may be expired after the requested
	// expiration time due to server lag).
	TTL int64
	// The resource version of the node that contained the returned object.
	ResourceVersion uint64
}

// MatchValue defines a pair (<index name>, <value for that index>).
type MatchValue struct {
	IndexName string
	Value     string
}

// TriggerPublisherFunc is a function that takes an object, and returns a list of pairs
// (<index name>, <index value for the given object>) for all indexes known
// to that function.
type TriggerPublisherFunc func(obj runtime.Object) []MatchValue

// Everything accepts all objects.
var Everything = SelectionPredicate{
	Label: labels.Everything(),
	Field: fields.Everything(),
}

// Pass an UpdateFunc to Interface.GuaranteedUpdate to make an update
// that is guaranteed to succeed.
// See the comment for GuaranteedUpdate for more details.
type UpdateFunc func(input runtime.Object, res ResponseMeta) (output runtime.Object, ttl *uint64, err error)

// Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.
type Preconditions struct {
	// Specifies the target UID.
	// +optional
	UID *types.UID `json:"uid,omitempty"`
	// Specifies the target ResourceVersion
	// +optional
	ResourceVersion *string `json:"resourceVersion,omitempty"`
}

// NewUIDPreconditions returns a Preconditions with UID set.
func NewUIDPreconditions(uid string) *Preconditions {
	u := types.UID(uid)
	return &Preconditions{UID: &u}
}

func (p *Preconditions) Check(key string, obj runtime.Object) error {
	if p == nil {
		return nil
	}
	objMeta, err := meta.Accessor(obj)
	if err != nil {
		return NewInternalErrorf(
			"can't enforce preconditions %v on un-introspectable object %v, got error: %v",
			*p,
			obj,
			err)
	}
	if p.UID != nil && *p.UID != objMeta.GetUID() {
		err := fmt.Sprintf(
			"Precondition failed: UID in precondition: %v, UID in object meta: %v",
			*p.UID,
			objMeta.GetUID())
		return NewInvalidObjError(key, err)
	}
	if p.ResourceVersion != nil && *p.ResourceVersion != objMeta.GetResourceVersion() {
		err := fmt.Sprintf(
			"Precondition failed: ResourceVersion in precondition: %v, ResourceVersion in object meta: %v",
			*p.ResourceVersion,
			objMeta.GetResourceVersion())
		return NewInvalidObjError(key, err)
	}
	return nil
}

// Interface offers a common interface for object marshaling/unmarshaling operations and
// hides all the storage-related operations behind it.
type Interface interface {
	// Returns Versioner associated with this interface.
	Versioner() Versioner

	// Create adds a new object at a key unless it already exists. 'ttl' is time-to-live
	// in seconds (0 means forever). If no error is returned and out is not nil, out will be
	// set to the read value from database.
	Create(ctx context.Context, key string, obj, out runtime.Object, ttl uint64) error

	// Delete removes the specified key and returns the value that existed at that spot.
	// If key didn't exist, it will return NotFound storage error.
	Delete(ctx context.Context, key string, out runtime.Object, preconditions *Preconditions) error

	// Watch begins watching the specified key. Events are decoded into API objects,
	// and any items selected by 'p' are sent down to returned watch.Interface.
	// resourceVersion may be used to specify what version to begin watching,
	// which should be the current resourceVersion, and no longer rv+1
	// (e.g. reconnecting without missing any updates).
	// If resource version is "0", this interface will get current object at given key
	// and send it in an "ADDED" event, before watch starts.
	Watch(ctx context.Context, key string, resourceVersion string, p SelectionPredicate) (watch.Interface, error)

	// WatchList begins watching the specified key's items. Items are decoded into API
	// objects and any item selected by 'p' are sent down to returned watch.Interface.
	// resourceVersion may be used to specify what version to begin watching,
	// which should be the current resourceVersion, and no longer rv+1
	// (e.g. reconnecting without missing any updates).
	// If resource version is "0", this interface will list current objects directory defined by key
	// and send them in "ADDED" events, before watch starts.
	WatchList(ctx context.Context, key string, resourceVersion string, p SelectionPredicate) (watch.Interface, error)

	// Get unmarshals json found at key into objPtr. On a not found error, will either
	// return a zero object of the requested type, or an error, depending on ignoreNotFound.
	// Treats empty responses and nil response nodes exactly like a not found error.
	// The returned contents may be delayed, but it is guaranteed that they will
	// be have at least 'resourceVersion'.
	Get(ctx context.Context, key string, resourceVersion string, objPtr runtime.Object, ignoreNotFound bool) error

	// GetToList unmarshals json found at key and opaque it into *List api object
	// (an object that satisfies the runtime.IsList definition).
	// The returned contents may be delayed, but it is guaranteed that they will
	// be have at least 'resourceVersion'.
	GetToList(ctx context.Context, key string, resourceVersion string, p SelectionPredicate, listObj runtime.Object) error

	// List unmarshalls jsons found at directory defined by key and opaque them
	// into *List api object (an object that satisfies runtime.IsList definition).
	// The returned contents may be delayed, but it is guaranteed that they will
	// be have at least 'resourceVersion'.
	List(ctx context.Context, key string, resourceVersion string, p SelectionPredicate, listObj runtime.Object) error

	// GuaranteedUpdate keeps calling 'tryUpdate()' to update key 'key' (of type 'ptrToType')
	// retrying the update until success if there is index conflict.
	// Note that object passed to tryUpdate may change across invocations of tryUpdate() if
	// other writers are simultaneously updating it, so tryUpdate() needs to take into account
	// the current contents of the object when deciding how the update object should look.
	// If the key doesn't exist, it will return NotFound storage error if ignoreNotFound=false
	// or zero value in 'ptrToType' parameter otherwise.
	// If the object to update has the same value as previous, it won't do any update
	// but will return the object in 'ptrToType' parameter.
	// If 'suggestion' can contain zero or one element - in such case this can be used as
	// a suggestion about the current version of the object to avoid read operation from
	// storage to get it.
	//
	// Example:
	//
	// s := /* implementation of Interface */
	// err := s.GuaranteedUpdate(
	//     "myKey", &MyType{}, true,
	//     func(input runtime.Object, res ResponseMeta) (runtime.Object, *uint64, error) {
	//       // Before each incovation of the user defined function, "input" is reset to
	//       // current contents for "myKey" in database.
	//       curr := input.(*MyType)  // Guaranteed to succeed.
	//
	//       // Make the modification
	//       curr.Counter++
	//
	//       // Return the modified object - return an error to stop iterating. Return
	//       // a uint64 to alter the TTL on the object, or nil to keep it the same value.
	//       return cur, nil, nil
	//    }
	// })
	GuaranteedUpdate(
		ctx context.Context, key string, ptrToType runtime.Object, ignoreNotFound bool,
		precondtions *Preconditions, tryUpdate UpdateFunc, suggestion ...runtime.Object) error

	// Count returns number of different entries under the key (generally being path prefix).
	Count(key string) (int64, error)
}
<|endoftext|>


<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 metrics

import (
	"github.com/blang/semver"
	"github.com/prometheus/client_golang/prometheus"
)

// Histogram is our internal representation for our wrapping struct around prometheus
// histograms. Summary implements both kubeCollector and ObserverMetric
type Histogram struct {
	ObserverMetric
	*HistogramOpts
	lazyMetric
	selfCollector
}

// NewHistogram returns an object which is Histogram-like. However, nothing
// will be measured until the histogram is registered somewhere.
func NewHistogram(opts *HistogramOpts) *Histogram {
	// todo: handle defaulting better
	if opts.StabilityLevel == "" {
		opts.StabilityLevel = ALPHA
	}
	h := &Histogram{
		HistogramOpts: opts,
		lazyMetric:    lazyMetric{},
	}
	h.setPrometheusHistogram(noopMetric{})
	h.lazyInit(h)
	return h
}

// setPrometheusHistogram sets the underlying KubeGauge object, i.e. the thing that does the measurement.
func (h *Histogram) setPrometheusHistogram(histogram prometheus.Histogram) {
	h.ObserverMetric = histogram
	h.initSelfCollection(histogram)
}

// DeprecatedVersion returns a pointer to the Version or nil
func (h *Histogram) DeprecatedVersion() *semver.Version {
	return parseSemver(h.HistogramOpts.DeprecatedVersion)
}

// initializeMetric invokes the actual prometheus.Histogram object instantiation
// and stores a reference to it
func (h *Histogram) initializeMetric() {
	h.HistogramOpts.annotateStabilityLevel()
	// this actually creates the underlying prometheus gauge.
	h.setPrometheusHistogram(prometheus.NewHistogram(h.HistogramOpts.toPromHistogramOpts()))
}

// initializeDeprecatedMetric invokes the actual prometheus.Histogram object instantiation
// but modifies the Help description prior to object instantiation.
func (h *Histogram) initializeDeprecatedMetric() {
	h.HistogramOpts.markDeprecated()
	h.initializeMetric()
}

// HistogramVec is the internal representation of our wrapping struct around prometheus
// histogramVecs.
type HistogramVec struct {
	*prometheus.HistogramVec
	*HistogramOpts
	lazyMetric
	originalLabels []string
}

// NewHistogramVec returns an object which satisfies kubeCollector and wraps the
// prometheus.HistogramVec object. However, the object returned will not measure
// anything unless the collector is first registered, since the metric is lazily instantiated.
func NewHistogramVec(opts *HistogramOpts, labels []string) *HistogramVec {
	// todo: handle defaulting better
	if opts.StabilityLevel == "" {
		opts.StabilityLevel = ALPHA
	}
	v := &HistogramVec{
		HistogramVec:   noopHistogramVec,
		HistogramOpts:  opts,
		originalLabels: labels,
		lazyMetric:     lazyMetric{},
	}
	v.lazyInit(v)
	return v
}

// DeprecatedVersion returns a pointer to the Version or nil
func (v *HistogramVec) DeprecatedVersion() *semver.Version {
	return parseSemver(v.HistogramOpts.DeprecatedVersion)
}

func (v *HistogramVec) initializeMetric() {
	v.HistogramOpts.annotateStabilityLevel()
	v.HistogramVec = prometheus.NewHistogramVec(v.HistogramOpts.toPromHistogramOpts(), v.originalLabels)
}

func (v *HistogramVec) initializeDeprecatedMetric() {
	v.HistogramOpts.markDeprecated()
	v.initializeMetric()
}

// Default Prometheus behavior actually results in the creation of a new metric
// if a metric with the unique label values is not found in the underlying stored metricMap.
// This means  that if this function is called but the underlying metric is not registered
// (which means it will never be exposed externally nor consumed), the metric will exist in memory
// for perpetuity (i.e. throughout application lifecycle).
//
// For reference: https://github.com/prometheus/client_golang/blob/v0.9.2/prometheus/histogram.go#L460-L470

// WithLabelValues returns the ObserverMetric for the given slice of label
// values (same order as the VariableLabels in Desc). If that combination of
// label values is accessed for the first time, a new ObserverMetric is created IFF the HistogramVec
// has been registered to a metrics registry.
func (v *HistogramVec) WithLabelValues(lvs ...string) ObserverMetric {
	if !v.IsCreated() {
		return noop
	}
	return v.HistogramVec.WithLabelValues(lvs...)
}

// With returns the ObserverMetric for the given Labels map (the label names
// must match those of the VariableLabels in Desc). If that label map is
// accessed for the first time, a new ObserverMetric is created IFF the HistogramVec has
// been registered to a metrics registry.
func (v *HistogramVec) With(labels prometheus.Labels) ObserverMetric {
	if !v.IsCreated() {
		return noop
	}
	return v.HistogramVec.With(labels)
}

// Delete deletes the metric where the variable labels are the same as those
// passed in as labels. It returns true if a metric was deleted.
//
// It is not an error if the number and names of the Labels are inconsistent
// with those of the VariableLabels in Desc. However, such inconsistent Labels
// can never match an actual metric, so the method will always return false in
// that case.
func (v *HistogramVec) Delete(labels prometheus.Labels) bool {
	if !v.IsCreated() {
		return false // since we haven't created the metric, we haven't deleted a metric with the passed in values
	}
	return v.HistogramVec.Delete(labels)
}
<commit_msg>Add wrapper to metrics stability framework for prometheus buckets functionality<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 metrics

import (
	"github.com/blang/semver"
	"github.com/prometheus/client_golang/prometheus"
)

// DefBuckets is a wrapper for prometheus.DefBuckets
var DefBuckets = prometheus.DefBuckets

// LinearBuckets is a wrapper for prometheus.LinearBuckets.
func LinearBuckets(start, width float64, count int) []float64 {
	return prometheus.LinearBuckets(start, width, count)
}

// ExponentialBuckets is a wrapper for prometheus.ExponentialBuckets.
func ExponentialBuckets(start, factor float64, count int) []float64 {
	return prometheus.ExponentialBuckets(start, factor, count)
}

// Histogram is our internal representation for our wrapping struct around prometheus
// histograms. Summary implements both kubeCollector and ObserverMetric
type Histogram struct {
	ObserverMetric
	*HistogramOpts
	lazyMetric
	selfCollector
}

// NewHistogram returns an object which is Histogram-like. However, nothing
// will be measured until the histogram is registered somewhere.
func NewHistogram(opts *HistogramOpts) *Histogram {
	// todo: handle defaulting better
	if opts.StabilityLevel == "" {
		opts.StabilityLevel = ALPHA
	}
	h := &Histogram{
		HistogramOpts: opts,
		lazyMetric:    lazyMetric{},
	}
	h.setPrometheusHistogram(noopMetric{})
	h.lazyInit(h)
	return h
}

// setPrometheusHistogram sets the underlying KubeGauge object, i.e. the thing that does the measurement.
func (h *Histogram) setPrometheusHistogram(histogram prometheus.Histogram) {
	h.ObserverMetric = histogram
	h.initSelfCollection(histogram)
}

// DeprecatedVersion returns a pointer to the Version or nil
func (h *Histogram) DeprecatedVersion() *semver.Version {
	return parseSemver(h.HistogramOpts.DeprecatedVersion)
}

// initializeMetric invokes the actual prometheus.Histogram object instantiation
// and stores a reference to it
func (h *Histogram) initializeMetric() {
	h.HistogramOpts.annotateStabilityLevel()
	// this actually creates the underlying prometheus gauge.
	h.setPrometheusHistogram(prometheus.NewHistogram(h.HistogramOpts.toPromHistogramOpts()))
}

// initializeDeprecatedMetric invokes the actual prometheus.Histogram object instantiation
// but modifies the Help description prior to object instantiation.
func (h *Histogram) initializeDeprecatedMetric() {
	h.HistogramOpts.markDeprecated()
	h.initializeMetric()
}

// HistogramVec is the internal representation of our wrapping struct around prometheus
// histogramVecs.
type HistogramVec struct {
	*prometheus.HistogramVec
	*HistogramOpts
	lazyMetric
	originalLabels []string
}

// NewHistogramVec returns an object which satisfies kubeCollector and wraps the
// prometheus.HistogramVec object. However, the object returned will not measure
// anything unless the collector is first registered, since the metric is lazily instantiated.
func NewHistogramVec(opts *HistogramOpts, labels []string) *HistogramVec {
	// todo: handle defaulting better
	if opts.StabilityLevel == "" {
		opts.StabilityLevel = ALPHA
	}
	v := &HistogramVec{
		HistogramVec:   noopHistogramVec,
		HistogramOpts:  opts,
		originalLabels: labels,
		lazyMetric:     lazyMetric{},
	}
	v.lazyInit(v)
	return v
}

// DeprecatedVersion returns a pointer to the Version or nil
func (v *HistogramVec) DeprecatedVersion() *semver.Version {
	return parseSemver(v.HistogramOpts.DeprecatedVersion)
}

func (v *HistogramVec) initializeMetric() {
	v.HistogramOpts.annotateStabilityLevel()
	v.HistogramVec = prometheus.NewHistogramVec(v.HistogramOpts.toPromHistogramOpts(), v.originalLabels)
}

func (v *HistogramVec) initializeDeprecatedMetric() {
	v.HistogramOpts.markDeprecated()
	v.initializeMetric()
}

// Default Prometheus behavior actually results in the creation of a new metric
// if a metric with the unique label values is not found in the underlying stored metricMap.
// This means  that if this function is called but the underlying metric is not registered
// (which means it will never be exposed externally nor consumed), the metric will exist in memory
// for perpetuity (i.e. throughout application lifecycle).
//
// For reference: https://github.com/prometheus/client_golang/blob/v0.9.2/prometheus/histogram.go#L460-L470

// WithLabelValues returns the ObserverMetric for the given slice of label
// values (same order as the VariableLabels in Desc). If that combination of
// label values is accessed for the first time, a new ObserverMetric is created IFF the HistogramVec
// has been registered to a metrics registry.
func (v *HistogramVec) WithLabelValues(lvs ...string) ObserverMetric {
	if !v.IsCreated() {
		return noop
	}
	return v.HistogramVec.WithLabelValues(lvs...)
}

// With returns the ObserverMetric for the given Labels map (the label names
// must match those of the VariableLabels in Desc). If that label map is
// accessed for the first time, a new ObserverMetric is created IFF the HistogramVec has
// been registered to a metrics registry.
func (v *HistogramVec) With(labels prometheus.Labels) ObserverMetric {
	if !v.IsCreated() {
		return noop
	}
	return v.HistogramVec.With(labels)
}

// Delete deletes the metric where the variable labels are the same as those
// passed in as labels. It returns true if a metric was deleted.
//
// It is not an error if the number and names of the Labels are inconsistent
// with those of the VariableLabels in Desc. However, such inconsistent Labels
// can never match an actual metric, so the method will always return false in
// that case.
func (v *HistogramVec) Delete(labels prometheus.Labels) bool {
	if !v.IsCreated() {
		return false // since we haven't created the metric, we haven't deleted a metric with the passed in values
	}
	return v.HistogramVec.Delete(labels)
}
<|endoftext|>


<commit_before>// Clock counts down to or up from a target time.
package main

import (
	"fmt"
	"os"
	"time"
)

// Update target and motto as desired.
var (
	target = time.Date(2019, 3, 1, 0, 0, 0, 0, time.UTC)
	motto  = "Just Go"
)

func main() {
	printTargetTime(target, motto)
	exitOnEnterKey()

	var previous time.Time
	for {
		now := time.Now().Truncate(time.Second)
		if now != previous {
			previous = now
			countdown := now.Sub(target) // Negative times are before the target
			printCountdown(now.In(target.Location()), countdown)
		}
		time.Sleep(100 * time.Millisecond)
	}
}

func exitOnEnterKey() {
	go func() {
		buf := make([]byte, 1)
		_, _ = os.Stdin.Read(buf)
		os.Exit(0)
	}()
}

const (
	highlightStart = "\x1b[1;35m"
	highlightEnd   = "\x1b[0m"
	indent         = "\t"
)

func printTargetTime(target time.Time, motto string) {
	fmt.Print(indent, highlightStart, motto, highlightEnd, "\n")
	fmt.Print(indent, target.Format(time.UnixDate), "\n")
}

func printCountdown(now time.Time, countdown time.Duration) {
	var sign string
	if countdown >= 0 {
		sign = "+"
	} else {
		sign = "-"
		countdown = -countdown
	}

	days := int(countdown / (24 * time.Hour))
	countdown = countdown % (24 * time.Hour)

	fmt.Print(indent, now.Format(time.UnixDate), "  ", sign)
	if days > 0 {
		fmt.Print(days, "d")
	}
	fmt.Print(countdown, "          \r")
	os.Stdout.Sync()
}
<commit_msg>local time<commit_after>// Clock counts down to or up from a target time.
package main

import (
	"fmt"
	"os"
	"time"
)

// Update target and motto as desired.
var (
	target = time.Date(2019, 3, 1, 0, 0, 0, 0, time.Local)
	motto  = "Just Go"
)

func main() {
	printTargetTime(target, motto)
	exitOnEnterKey()

	var previous time.Time
	for {
		now := time.Now().Truncate(time.Second)
		if now != previous {
			previous = now
			countdown := now.Sub(target) // Negative times are before the target
			printCountdown(now.In(target.Location()), countdown)
		}
		time.Sleep(100 * time.Millisecond)
	}
}

func exitOnEnterKey() {
	go func() {
		buf := make([]byte, 1)
		_, _ = os.Stdin.Read(buf)
		os.Exit(0)
	}()
}

const (
	highlightStart = "\x1b[1;35m"
	highlightEnd   = "\x1b[0m"
	indent         = "\t"
)

func printTargetTime(target time.Time, motto string) {
	fmt.Print(indent, highlightStart, motto, highlightEnd, "\n")
	fmt.Print(indent, target.Format(time.UnixDate), "\n")
}

func printCountdown(now time.Time, countdown time.Duration) {
	var sign string
	if countdown >= 0 {
		sign = "+"
	} else {
		sign = "-"
		countdown = -countdown
	}

	days := int(countdown / (24 * time.Hour))
	countdown = countdown % (24 * time.Hour)

	fmt.Print(indent, now.Format(time.UnixDate), "  ", sign)
	if days > 0 {
		fmt.Print(days, "d")
	}
	fmt.Print(countdown, "          \r")
	os.Stdout.Sync()
}
<|endoftext|>


<commit_before>package events

import (
	"io/ioutil"
	"path"
	"testing"
	"time"

	"go.skia.org/infra/go/testutils"

	assert "github.com/stretchr/testify/require"
)

func TestAggregateMetric(t *testing.T) {
	testutils.SmallTest(t)

	tmp, err := ioutil.TempDir("", "")
	assert.NoError(t, err)
	defer testutils.RemoveAll(t, tmp)

	db, err := NewEventDB(path.Join(tmp, "events.bdb"))
	assert.NoError(t, err)
	m, err := NewEventMetrics(db)
	assert.NoError(t, err)

	s := "my-events"
	now := time.Now()
	k1 := now.Add(-3 * time.Second)
	v1 := 0.05
	assert.NoError(t, m.db.Insert(&Event{
		Stream:    s,
		Timestamp: k1,
		Data:      encodeEvent(v1),
	}))

	period := 20 * time.Minute
	called := false
	assert.NoError(t, m.AggregateMetric(s, nil, period, func(vs []*Event) (float64, error) {
		called = true
		assert.Equal(t, 1, len(vs))
		assert.Equal(t, v1, decodeEvent(vs[0].Data))
		return 0.0, nil
	}))
	assert.False(t, called)
	assert.NoError(t, m.updateMetrics(now))
	assert.True(t, called)
}
<commit_msg>Up TestAggregateMetric to MediumTest.<commit_after>package events

import (
	"io/ioutil"
	"path"
	"testing"
	"time"

	"go.skia.org/infra/go/testutils"

	assert "github.com/stretchr/testify/require"
)

func TestAggregateMetric(t *testing.T) {
	testutils.MediumTest(t)

	tmp, err := ioutil.TempDir("", "")
	assert.NoError(t, err)
	defer testutils.RemoveAll(t, tmp)

	db, err := NewEventDB(path.Join(tmp, "events.bdb"))
	assert.NoError(t, err)
	m, err := NewEventMetrics(db)
	assert.NoError(t, err)

	s := "my-events"
	now := time.Now()
	k1 := now.Add(-3 * time.Second)
	v1 := 0.05
	assert.NoError(t, m.db.Insert(&Event{
		Stream:    s,
		Timestamp: k1,
		Data:      encodeEvent(v1),
	}))

	period := 20 * time.Minute
	called := false
	assert.NoError(t, m.AggregateMetric(s, nil, period, func(vs []*Event) (float64, error) {
		called = true
		assert.Equal(t, 1, len(vs))
		assert.Equal(t, v1, decodeEvent(vs[0].Data))
		return 0.0, nil
	}))
	assert.False(t, called)
	assert.NoError(t, m.updateMetrics(now))
	assert.True(t, called)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"socialapi/models"
)

func testHistoryOperations() {
	channel, err := createChannel()
	if err != nil {
		fmt.Println("error while creating channel", err)
		err = nil
	}

	CHANNEL_ID = channel.Id

	for i := 0; i < 10; i++ {
		channelParticipant, err := createChannelParticipant(channel.Id)
		if err != nil {
			fmt.Println("error while creating channelParticipant 1", err)
			err = nil
			return
		}

		ACCOUNT_ID = channelParticipant.AccountId

		_, err = createPost()
		if err != nil {
			fmt.Println("error while creating post", err)
			err = nil
		}

		history, err := getHistory(CHANNEL_ID, ACCOUNT_ID)
		if err != nil {
			fmt.Println("Error while getting the history", err)
		}
		if history == nil {
			fmt.Print("history is nil")
			return
		}
		if len(history.MessageList) != i+1 {
			fmt.Println("history length is not OK!", len(history.MessageList), i+1)
		}
	}

}

func getHistory(channelId, accountId int64) (*models.HistoryResponse, error) {
	url := fmt.Sprintf("/channel/%d/history?accountId=%d", channelId, accountId)
	res, err := sendRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}

	var history models.HistoryResponse
	err = json.Unmarshal(res, &history)
	if err != nil {
		return nil, err
	}

	return &history, nil
}
<commit_msg>Social: inject channel id and account id into create post function<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"socialapi/models"
)

func testHistoryOperations() {
	channel, err := createChannel()
	if err != nil {
		fmt.Println("error while creating channel", err)
		err = nil
	}

	CHANNEL_ID = channel.Id

	for i := 0; i < 10; i++ {
		channelParticipant, err := createChannelParticipant(channel.Id)
		if err != nil {
			fmt.Println("error while creating channelParticipant 1", err)
			err = nil
			return
		}

		ACCOUNT_ID = channelParticipant.AccountId

		_, err = createPost(CHANNEL_ID, ACCOUNT_ID)
		if err != nil {
			fmt.Println("error while creating post", err)
			err = nil
		}

		history, err := getHistory(CHANNEL_ID, ACCOUNT_ID)
		if err != nil {
			fmt.Println("Error while getting the history", err)
		}
		if history == nil {
			fmt.Print("history is nil")
			return
		}
		if len(history.MessageList) != i+1 {
			fmt.Println("history length is not OK!", len(history.MessageList), i+1)
		}
	}

}

func getHistory(channelId, accountId int64) (*models.HistoryResponse, error) {
	url := fmt.Sprintf("/channel/%d/history?accountId=%d", channelId, accountId)
	res, err := sendRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}

	var history models.HistoryResponse
	err = json.Unmarshal(res, &history)
	if err != nil {
		return nil, err
	}

	return &history, nil
}
<|endoftext|>


<commit_before>package cmdns

import (
	"errors"
	"fmt"

	"github.com/spf13/cobra"
)

var (
	// DefaultNamespacer is the default namespacer for the package
	DefaultNamespacer = New()

	// DefaultNamespaceSeparator is the char that seperates commands
	DefaultNamespaceSeparator = ":"
)

// SetOverrideUsageFunc when set to true will overide the command's usage function with the package's usage function that displays namespaces using the default namespacer
func SetOverrideUsageFunc(v bool) *CobraNamespacer {
	return DefaultNamespacer.SetOverrideUsageFunc(v)
}

// Namespace enables namespacing for the command using the DefaultCmdNS
func Namespace(cmd *cobra.Command) error {
	return DefaultNamespacer.Namespace(cmd)
}

// CobraNamespacer is the struct represting the component that namespaces cobra's sucommands
type CobraNamespacer struct {
	// Namespaces is the collection of cobra namespaces
	Namespaces        []*CobraNamespace
	OverrideUsageFunc bool
}

// New returns a new instance of the CobraNamespacer
func New() *CobraNamespacer {
	return &CobraNamespacer{
		Namespaces:        make([]*CobraNamespace, 0),
		OverrideUsageFunc: true,
	}
}

// SetOverrideUsageFunc when set to true will overide the command's usage
// function with the package's usage function that displays namespaces
func (c *CobraNamespacer) SetOverrideUsageFunc(v bool) *CobraNamespacer {
	c.OverrideUsageFunc = v
	return c
}

// Namespace enables namespacing for the command's subcommands
func (c *CobraNamespacer) Namespace(cmd *cobra.Command) error {
	if cmd == nil {
		return errors.New("cmdns: cmd cannot be nil")
	}
	for _, child := range cmd.Commands() {
		n := NewCobraNamespace()
		n.OverrideUsageFunc = c.OverrideUsageFunc
		c.Namespaces = append(c.Namespaces, n)
		if err := n.Namespace(child); err != nil {
			return err
		}
	}
	return nil
}

// CobraNamespace represents a namespace for a command. This is usually then second level command.
type CobraNamespace struct {
	OverrideUsageFunc bool

	cmd      *cobra.Command
	commands []*cobra.Command
}

// NewCobraNamespace returns a new Namespace
func NewCobraNamespace() *CobraNamespace {
	return &CobraNamespace{
		OverrideUsageFunc: true,
		commands:          make([]*cobra.Command, 0),
	}
}

// AvailableCommands returns the namespaced commands that are available
func (n *CobraNamespace) AvailableCommands() []*cobra.Command {
	return n.commands
}

// Command returns the command for the namespace
func (n *CobraNamespace) Command() *cobra.Command {
	return n.cmd
}

// Namespace enables namespacing for a sub-commmand and its immediated children. It returns an error if the command does not have a parent.
func (n *CobraNamespace) Namespace(cmd *cobra.Command) error {
	if !cmd.HasParent() {
		return errors.New("cmdns: command requires a parent")
	}

	// Do not bind if there are not available sub commands
	if !cmd.HasAvailableSubCommands() {
		return nil
	}

	if n.OverrideUsageFunc {
		cmd.SetUsageFunc(n.UsageFunc())
	}

	for _, c := range cmd.Commands() {
		if !c.IsAvailableCommand() {
			continue
		}
		// copy the command add it to the root command with a prefix of its parent.
		nc := *c
		nc.Use = cmd.Name() + DefaultNamespaceSeparator + c.Use

		// add this command to the root and hide it so it does not show in available commands list
		c.Parent().Parent().AddCommand(&nc)
		c.Hidden = true
		n.commands = append(n.commands, &nc)
	}
	n.cmd = cmd
	return nil
}

// UsageFunc returns the usage function for the command that renders namespaces
func (n *CobraNamespace) UsageFunc() (f func(*cobra.Command) error) {
	return func(*cobra.Command) error {
		err := tmpl(n.Command().Out(), usageTemplate, n)
		if err != nil {
			fmt.Print(err)
		}
		return err
	}
}

var usageTemplate = `{{$ns := .}}{{with .Command}}Usage: {{if .Runnable}}{{.UseLine}}{{if .HasFlags}} [flags]{{end}}{{end}}{{if gt .Aliases 0}}

Aliases:
  {{.NameAndAliases}}
{{end}}{{if .HasExample}}

Examples:
  {{ .Example }}{{end}}{{ if .HasAvailableSubCommands}}

Additional commands, use "{{.Parent.CommandPath}} COMMAND --help" for more information about a command.
{{range $ns.AvailableCommands}}
  {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{ if .HasLocalFlags}}

Flags:
{{.LocalFlags.FlagUsages | trimRightSpace}}{{end}}{{ if .HasInheritedFlags}}

Global Flags:
{{.InheritedFlags.FlagUsages | trimRightSpace}}{{end}}{{if .HasHelpSubCommands}}

Additional help topics:{{range .Commands}}{{if .IsHelpCommand}}
  {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}
{{end}}
`
<commit_msg>Fix compatibility with cobra (#3)<commit_after>package cmdns

import (
	"errors"
	"fmt"

	"github.com/spf13/cobra"
)

var (
	// DefaultNamespacer is the default namespacer for the package
	DefaultNamespacer = New()

	// DefaultNamespaceSeparator is the char that seperates commands
	DefaultNamespaceSeparator = ":"
)

// SetOverrideUsageFunc when set to true will overide the command's usage function with the package's usage function that displays namespaces using the default namespacer
func SetOverrideUsageFunc(v bool) *CobraNamespacer {
	return DefaultNamespacer.SetOverrideUsageFunc(v)
}

// Namespace enables namespacing for the command using the DefaultCmdNS
func Namespace(cmd *cobra.Command) error {
	return DefaultNamespacer.Namespace(cmd)
}

// CobraNamespacer is the struct represting the component that namespaces cobra's sucommands
type CobraNamespacer struct {
	// Namespaces is the collection of cobra namespaces
	Namespaces        []*CobraNamespace
	OverrideUsageFunc bool
}

// New returns a new instance of the CobraNamespacer
func New() *CobraNamespacer {
	return &CobraNamespacer{
		Namespaces:        make([]*CobraNamespace, 0),
		OverrideUsageFunc: true,
	}
}

// SetOverrideUsageFunc when set to true will overide the command's usage
// function with the package's usage function that displays namespaces
func (c *CobraNamespacer) SetOverrideUsageFunc(v bool) *CobraNamespacer {
	c.OverrideUsageFunc = v
	return c
}

// Namespace enables namespacing for the command's subcommands
func (c *CobraNamespacer) Namespace(cmd *cobra.Command) error {
	if cmd == nil {
		return errors.New("cmdns: cmd cannot be nil")
	}
	for _, child := range cmd.Commands() {
		n := NewCobraNamespace()
		n.OverrideUsageFunc = c.OverrideUsageFunc
		c.Namespaces = append(c.Namespaces, n)
		if err := n.Namespace(child); err != nil {
			return err
		}
	}
	return nil
}

// CobraNamespace represents a namespace for a command. This is usually then second level command.
type CobraNamespace struct {
	OverrideUsageFunc bool

	cmd      *cobra.Command
	commands []*cobra.Command
}

// NewCobraNamespace returns a new Namespace
func NewCobraNamespace() *CobraNamespace {
	return &CobraNamespace{
		OverrideUsageFunc: true,
		commands:          make([]*cobra.Command, 0),
	}
}

// AvailableCommands returns the namespaced commands that are available
func (n *CobraNamespace) AvailableCommands() []*cobra.Command {
	return n.commands
}

// Command returns the command for the namespace
func (n *CobraNamespace) Command() *cobra.Command {
	return n.cmd
}

// Namespace enables namespacing for a sub-commmand and its immediated children. It returns an error if the command does not have a parent.
func (n *CobraNamespace) Namespace(cmd *cobra.Command) error {
	if !cmd.HasParent() {
		return errors.New("cmdns: command requires a parent")
	}

	// Do not bind if there are not available sub commands
	if !cmd.HasAvailableSubCommands() {
		return nil
	}

	if n.OverrideUsageFunc {
		cmd.SetUsageFunc(n.UsageFunc())
	}

	for _, c := range cmd.Commands() {
		if !c.IsAvailableCommand() {
			continue
		}
		// copy the command add it to the root command with a prefix of its parent.
		nc := *c
		nc.Use = cmd.Name() + DefaultNamespaceSeparator + c.Use

		// add this command to the root and hide it so it does not show in available commands list
		c.Parent().Parent().AddCommand(&nc)
		c.Hidden = true
		n.commands = append(n.commands, &nc)
	}
	n.cmd = cmd
	return nil
}

// UsageFunc returns the usage function for the command that renders namespaces
func (n *CobraNamespace) UsageFunc() (f func(*cobra.Command) error) {
	return func(*cobra.Command) error {
		err := tmpl(n.Command().OutOrStdout(), usageTemplate, n)
		if err != nil {
			fmt.Print(err)
		}
		return err
	}
}

var usageTemplate = `{{$ns := .}}{{with .Command}}Usage: {{if .Runnable}}{{.UseLine}}{{if .HasFlags}} [flags]{{end}}{{end}}{{if gt .Aliases 0}}

Aliases:
  {{.NameAndAliases}}
{{end}}{{if .HasExample}}

Examples:
  {{ .Example }}{{end}}{{ if .HasAvailableSubCommands}}

Additional commands, use "{{.Parent.CommandPath}} COMMAND --help" for more information about a command.
{{range $ns.AvailableCommands}}
  {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{ if .HasLocalFlags}}

Flags:
{{.LocalFlags.FlagUsages | trimRightSpace}}{{end}}{{ if .HasInheritedFlags}}

Global Flags:
{{.InheritedFlags.FlagUsages | trimRightSpace}}{{end}}{{if .HasHelpSubCommands}}

Additional help topics:{{range .Commands}}{{if .IsHelpCommand}}
  {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}
{{end}}
`
<|endoftext|>


<commit_before>package gmf

/*

#cgo pkg-config: libavcodec

#include <stdlib.h>
#include "libavcodec/avcodec.h"
#include "libavutil/pixfmt.h"

*/
import "C"

import (
	"errors"
	"fmt"
	"unsafe"
)

var (
	AVMEDIA_TYPE_AUDIO int32 = C.AVMEDIA_TYPE_AUDIO
	AVMEDIA_TYPE_VIDEO int32 = C.AVMEDIA_TYPE_VIDEO

	AV_PIX_FMT_BGR24    int32 = C.AV_PIX_FMT_BGR24
	AV_PIX_FMT_BGR32    int32 = C.AV_PIX_FMT_BGR32
	AV_PIX_FMT_GRAY8    int32 = C.AV_PIX_FMT_GRAY8
	AV_PIX_FMT_RGB24    int32 = C.AV_PIX_FMT_RGB24
	AV_PIX_FMT_RGB32    int32 = C.AV_PIX_FMT_RGB32
	AV_PIX_FMT_YUV410P  int32 = C.AV_PIX_FMT_YUV410P
	AV_PIX_FMT_YUV411P  int32 = C.AV_PIX_FMT_YUV411P
	AV_PIX_FMT_YUV420P  int32 = C.AV_PIX_FMT_YUV420P
	AV_PIX_FMT_YUV422P  int32 = C.AV_PIX_FMT_YUV422P
	AV_PIX_FMT_YUV444P  int32 = C.AV_PIX_FMT_YUV444P
	AV_PIX_FMT_YUVJ420P int32 = C.AV_PIX_FMT_YUVJ420P
	AV_PIX_FMT_YUYV422  int32 = C.AV_PIX_FMT_YUYV422
	AV_PIX_FMT_NONE     int32 = C.AV_PIX_FMT_NONE

	FF_PROFILE_AAC_MAIN      int = C.FF_PROFILE_AAC_MAIN
	FF_PROFILE_AAC_LOW       int = C.FF_PROFILE_AAC_LOW
	FF_PROFILE_AAC_SSR       int = C.FF_PROFILE_AAC_SSR
	FF_PROFILE_AAC_LTP       int = C.FF_PROFILE_AAC_LTP
	FF_PROFILE_AAC_HE        int = C.FF_PROFILE_AAC_HE
	FF_PROFILE_AAC_HE_V2     int = C.FF_PROFILE_AAC_HE_V2
	FF_PROFILE_AAC_LD        int = C.FF_PROFILE_AAC_LD
	FF_PROFILE_AAC_ELD       int = C.FF_PROFILE_AAC_ELD
	FF_PROFILE_MPEG2_AAC_LOW int = C.FF_PROFILE_MPEG2_AAC_LOW
	FF_PROFILE_MPEG2_AAC_HE  int = C.FF_PROFILE_MPEG2_AAC_HE

	FF_PROFILE_DTS         int = C.FF_PROFILE_DTS
	FF_PROFILE_DTS_ES      int = C.FF_PROFILE_DTS_ES
	FF_PROFILE_DTS_96_24   int = C.FF_PROFILE_DTS_96_24
	FF_PROFILE_DTS_HD_HRA  int = C.FF_PROFILE_DTS_HD_HRA
	FF_PROFILE_DTS_HD_MA   int = C.FF_PROFILE_DTS_HD_MA
	FF_PROFILE_DTS_EXPRESS int = C.FF_PROFILE_DTS_EXPRESS

	FF_PROFILE_MPEG2_422          int = C.FF_PROFILE_MPEG2_422
	FF_PROFILE_MPEG2_HIGH         int = C.FF_PROFILE_MPEG2_HIGH
	FF_PROFILE_MPEG2_SS           int = C.FF_PROFILE_MPEG2_SS
	FF_PROFILE_MPEG2_SNR_SCALABLE int = C.FF_PROFILE_MPEG2_SNR_SCALABLE
	FF_PROFILE_MPEG2_MAIN         int = C.FF_PROFILE_MPEG2_MAIN
	FF_PROFILE_MPEG2_SIMPLE       int = C.FF_PROFILE_MPEG2_SIMPLE

	FF_PROFILE_H264_BASELINE            int = C.FF_PROFILE_H264_BASELINE
	FF_PROFILE_H264_MAIN                int = C.FF_PROFILE_H264_MAIN
	FF_PROFILE_H264_EXTENDED            int = C.FF_PROFILE_H264_EXTENDED
	FF_PROFILE_H264_HIGH                int = C.FF_PROFILE_H264_HIGH
	FF_PROFILE_H264_HIGH_10             int = C.FF_PROFILE_H264_HIGH_10
	FF_PROFILE_H264_HIGH_422            int = C.FF_PROFILE_H264_HIGH_422
	FF_PROFILE_H264_HIGH_444            int = C.FF_PROFILE_H264_HIGH_444
	FF_PROFILE_H264_HIGH_444_PREDICTIVE int = C.FF_PROFILE_H264_HIGH_444_PREDICTIVE
	FF_PROFILE_H264_CAVLC_444           int = C.FF_PROFILE_H264_CAVLC_444

	FF_PROFILE_VC1_SIMPLE   int = C.FF_PROFILE_VC1_SIMPLE
	FF_PROFILE_VC1_MAIN     int = C.FF_PROFILE_VC1_MAIN
	FF_PROFILE_VC1_COMPLEX  int = C.FF_PROFILE_VC1_COMPLEX
	FF_PROFILE_VC1_ADVANCED int = C.FF_PROFILE_VC1_ADVANCED

	FF_PROFILE_MPEG4_SIMPLE                    int = C.FF_PROFILE_MPEG4_SIMPLE
	FF_PROFILE_MPEG4_SIMPLE_SCALABLE           int = C.FF_PROFILE_MPEG4_SIMPLE_SCALABLE
	FF_PROFILE_MPEG4_CORE                      int = C.FF_PROFILE_MPEG4_CORE
	FF_PROFILE_MPEG4_MAIN                      int = C.FF_PROFILE_MPEG4_MAIN
	FF_PROFILE_MPEG4_N_BIT                     int = C.FF_PROFILE_MPEG4_N_BIT
	FF_PROFILE_MPEG4_SCALABLE_TEXTURE          int = C.FF_PROFILE_MPEG4_SCALABLE_TEXTURE
	FF_PROFILE_MPEG4_SIMPLE_FACE_ANIMATION     int = C.FF_PROFILE_MPEG4_SIMPLE_FACE_ANIMATION
	FF_PROFILE_MPEG4_BASIC_ANIMATED_TEXTURE    int = C.FF_PROFILE_MPEG4_BASIC_ANIMATED_TEXTURE
	FF_PROFILE_MPEG4_HYBRID                    int = C.FF_PROFILE_MPEG4_HYBRID
	FF_PROFILE_MPEG4_ADVANCED_REAL_TIME        int = C.FF_PROFILE_MPEG4_ADVANCED_REAL_TIME
	FF_PROFILE_MPEG4_CORE_SCALABLE             int = C.FF_PROFILE_MPEG4_CORE_SCALABLE
	FF_PROFILE_MPEG4_ADVANCED_CODING           int = C.FF_PROFILE_MPEG4_ADVANCED_CODING
	FF_PROFILE_MPEG4_ADVANCED_CORE             int = C.FF_PROFILE_MPEG4_ADVANCED_CORE
	FF_PROFILE_MPEG4_ADVANCED_SCALABLE_TEXTURE int = C.FF_PROFILE_MPEG4_ADVANCED_SCALABLE_TEXTURE
	FF_PROFILE_MPEG4_SIMPLE_STUDIO             int = C.FF_PROFILE_MPEG4_SIMPLE_STUDIO
	FF_PROFILE_MPEG4_ADVANCED_SIMPLE           int = C.FF_PROFILE_MPEG4_ADVANCED_SIMPLE

	AV_NOPTS_VALUE uint64 = C.AV_NOPTS_VALUE

	FF_PROFILE_JPEG2000_CSTREAM_RESTRICTION_0  int = C.FF_PROFILE_JPEG2000_CSTREAM_RESTRICTION_0
	FF_PROFILE_JPEG2000_CSTREAM_RESTRICTION_1  int = C.FF_PROFILE_JPEG2000_CSTREAM_RESTRICTION_1
	FF_PROFILE_JPEG2000_CSTREAM_NO_RESTRICTION int = C.FF_PROFILE_JPEG2000_CSTREAM_NO_RESTRICTION
	FF_PROFILE_JPEG2000_DCINEMA_2K             int = C.FF_PROFILE_JPEG2000_DCINEMA_2K
	FF_PROFILE_JPEG2000_DCINEMA_4K             int = C.FF_PROFILE_JPEG2000_DCINEMA_4K

	FF_PROFILE_VP9_0 int = C.FF_PROFILE_VP9_0
	FF_PROFILE_VP9_1 int = C.FF_PROFILE_VP9_1
	FF_PROFILE_VP9_2 int = C.FF_PROFILE_VP9_2
	FF_PROFILE_VP9_3 int = C.FF_PROFILE_VP9_3

	FF_PROFILE_HEVC_MAIN               int = C.FF_PROFILE_HEVC_MAIN
	FF_PROFILE_HEVC_MAIN_10            int = C.FF_PROFILE_HEVC_MAIN_10
	FF_PROFILE_HEVC_MAIN_STILL_PICTURE int = C.FF_PROFILE_HEVC_MAIN_STILL_PICTURE
	FF_PROFILE_HEVC_REXT               int = C.FF_PROFILE_HEVC_REXT
)

func init() {
	C.avcodec_register_all()
	InitDesc()
}

type Codec struct {
	avCodec *C.struct_AVCodec
	CgoMemoryManage
}

func FindDecoder(i interface{}) (*Codec, error) {
	var avc *C.struct_AVCodec

	switch t := i.(type) {
	case string:
		cname := C.CString(i.(string))
		defer C.free(unsafe.Pointer(cname))

		avc = C.avcodec_find_decoder_by_name(cname)
		break

	case int:
		avc = C.avcodec_find_decoder(uint32(i.(int)))
		break

	default:
		return nil, errors.New(fmt.Sprintf("Unable to find codec, unexpected arguments type '%v'", t))
	}

	if avc == nil {
		return nil, errors.New(fmt.Sprintf("Unable to find codec by value '%v'", i))
	}

	return &Codec{avCodec: avc}, nil
}

func FindEncoder(i interface{}) (*Codec, error) {
	var avc *C.struct_AVCodec

	switch t := i.(type) {
	case string:
		cname := C.CString(i.(string))
		defer C.free(unsafe.Pointer(cname))

		avc = C.avcodec_find_encoder_by_name(cname)
		break

	case int:
		avc = C.avcodec_find_encoder(uint32(i.(int)))
		break

	default:
		return nil, errors.New(fmt.Sprintf("Unable to find codec, unexpected arguments type '%v'", t))
	}

	if avc == nil {
		return nil, errors.New(fmt.Sprintf("Unable to find codec by value '%v'", i))
	}

	return &Codec{avCodec: avc}, nil
}

func (this *Codec) Free() {
	//nothing to do
}
func (this *Codec) Id() int {
	return int(this.avCodec.id)
}

func (this *Codec) Name() string {
	return C.GoString(this.avCodec.name)
}

func (this *Codec) LongName() string {
	return C.GoString(this.avCodec.long_name)
}

func (this *Codec) Type() int {
	// > ...field names that are keywords in Go can be
	// > accessed by prefixing them with an underscore
	return int(this.avCodec._type)
}

func (this *Codec) IsExperimental() bool {
	return bool((this.avCodec.capabilities & C.CODEC_CAP_EXPERIMENTAL) != 0)
}
<commit_msg>Revert "Fix int64 overflow with recent ffmpeg"<commit_after>package gmf

/*

#cgo pkg-config: libavcodec

#include <stdlib.h>
#include "libavcodec/avcodec.h"
#include "libavutil/pixfmt.h"

*/
import "C"

import (
	"errors"
	"fmt"
	"unsafe"
)

var (
	AVMEDIA_TYPE_AUDIO int32 = C.AVMEDIA_TYPE_AUDIO
	AVMEDIA_TYPE_VIDEO int32 = C.AVMEDIA_TYPE_VIDEO

	AV_PIX_FMT_BGR24    int32 = C.AV_PIX_FMT_BGR24
	AV_PIX_FMT_BGR32    int32 = C.AV_PIX_FMT_BGR32
	AV_PIX_FMT_GRAY8    int32 = C.AV_PIX_FMT_GRAY8
	AV_PIX_FMT_RGB24    int32 = C.AV_PIX_FMT_RGB24
	AV_PIX_FMT_RGB32    int32 = C.AV_PIX_FMT_RGB32
	AV_PIX_FMT_YUV410P  int32 = C.AV_PIX_FMT_YUV410P
	AV_PIX_FMT_YUV411P  int32 = C.AV_PIX_FMT_YUV411P
	AV_PIX_FMT_YUV420P  int32 = C.AV_PIX_FMT_YUV420P
	AV_PIX_FMT_YUV422P  int32 = C.AV_PIX_FMT_YUV422P
	AV_PIX_FMT_YUV444P  int32 = C.AV_PIX_FMT_YUV444P
	AV_PIX_FMT_YUVJ420P int32 = C.AV_PIX_FMT_YUVJ420P
	AV_PIX_FMT_YUYV422  int32 = C.AV_PIX_FMT_YUYV422
	AV_PIX_FMT_NONE     int32 = C.AV_PIX_FMT_NONE

	FF_PROFILE_AAC_MAIN      int = C.FF_PROFILE_AAC_MAIN
	FF_PROFILE_AAC_LOW       int = C.FF_PROFILE_AAC_LOW
	FF_PROFILE_AAC_SSR       int = C.FF_PROFILE_AAC_SSR
	FF_PROFILE_AAC_LTP       int = C.FF_PROFILE_AAC_LTP
	FF_PROFILE_AAC_HE        int = C.FF_PROFILE_AAC_HE
	FF_PROFILE_AAC_HE_V2     int = C.FF_PROFILE_AAC_HE_V2
	FF_PROFILE_AAC_LD        int = C.FF_PROFILE_AAC_LD
	FF_PROFILE_AAC_ELD       int = C.FF_PROFILE_AAC_ELD
	FF_PROFILE_MPEG2_AAC_LOW int = C.FF_PROFILE_MPEG2_AAC_LOW
	FF_PROFILE_MPEG2_AAC_HE  int = C.FF_PROFILE_MPEG2_AAC_HE

	FF_PROFILE_DTS         int = C.FF_PROFILE_DTS
	FF_PROFILE_DTS_ES      int = C.FF_PROFILE_DTS_ES
	FF_PROFILE_DTS_96_24   int = C.FF_PROFILE_DTS_96_24
	FF_PROFILE_DTS_HD_HRA  int = C.FF_PROFILE_DTS_HD_HRA
	FF_PROFILE_DTS_HD_MA   int = C.FF_PROFILE_DTS_HD_MA
	FF_PROFILE_DTS_EXPRESS int = C.FF_PROFILE_DTS_EXPRESS

	FF_PROFILE_MPEG2_422          int = C.FF_PROFILE_MPEG2_422
	FF_PROFILE_MPEG2_HIGH         int = C.FF_PROFILE_MPEG2_HIGH
	FF_PROFILE_MPEG2_SS           int = C.FF_PROFILE_MPEG2_SS
	FF_PROFILE_MPEG2_SNR_SCALABLE int = C.FF_PROFILE_MPEG2_SNR_SCALABLE
	FF_PROFILE_MPEG2_MAIN         int = C.FF_PROFILE_MPEG2_MAIN
	FF_PROFILE_MPEG2_SIMPLE       int = C.FF_PROFILE_MPEG2_SIMPLE

	FF_PROFILE_H264_BASELINE            int = C.FF_PROFILE_H264_BASELINE
	FF_PROFILE_H264_MAIN                int = C.FF_PROFILE_H264_MAIN
	FF_PROFILE_H264_EXTENDED            int = C.FF_PROFILE_H264_EXTENDED
	FF_PROFILE_H264_HIGH                int = C.FF_PROFILE_H264_HIGH
	FF_PROFILE_H264_HIGH_10             int = C.FF_PROFILE_H264_HIGH_10
	FF_PROFILE_H264_HIGH_422            int = C.FF_PROFILE_H264_HIGH_422
	FF_PROFILE_H264_HIGH_444            int = C.FF_PROFILE_H264_HIGH_444
	FF_PROFILE_H264_HIGH_444_PREDICTIVE int = C.FF_PROFILE_H264_HIGH_444_PREDICTIVE
	FF_PROFILE_H264_CAVLC_444           int = C.FF_PROFILE_H264_CAVLC_444

	FF_PROFILE_VC1_SIMPLE   int = C.FF_PROFILE_VC1_SIMPLE
	FF_PROFILE_VC1_MAIN     int = C.FF_PROFILE_VC1_MAIN
	FF_PROFILE_VC1_COMPLEX  int = C.FF_PROFILE_VC1_COMPLEX
	FF_PROFILE_VC1_ADVANCED int = C.FF_PROFILE_VC1_ADVANCED

	FF_PROFILE_MPEG4_SIMPLE                    int = C.FF_PROFILE_MPEG4_SIMPLE
	FF_PROFILE_MPEG4_SIMPLE_SCALABLE           int = C.FF_PROFILE_MPEG4_SIMPLE_SCALABLE
	FF_PROFILE_MPEG4_CORE                      int = C.FF_PROFILE_MPEG4_CORE
	FF_PROFILE_MPEG4_MAIN                      int = C.FF_PROFILE_MPEG4_MAIN
	FF_PROFILE_MPEG4_N_BIT                     int = C.FF_PROFILE_MPEG4_N_BIT
	FF_PROFILE_MPEG4_SCALABLE_TEXTURE          int = C.FF_PROFILE_MPEG4_SCALABLE_TEXTURE
	FF_PROFILE_MPEG4_SIMPLE_FACE_ANIMATION     int = C.FF_PROFILE_MPEG4_SIMPLE_FACE_ANIMATION
	FF_PROFILE_MPEG4_BASIC_ANIMATED_TEXTURE    int = C.FF_PROFILE_MPEG4_BASIC_ANIMATED_TEXTURE
	FF_PROFILE_MPEG4_HYBRID                    int = C.FF_PROFILE_MPEG4_HYBRID
	FF_PROFILE_MPEG4_ADVANCED_REAL_TIME        int = C.FF_PROFILE_MPEG4_ADVANCED_REAL_TIME
	FF_PROFILE_MPEG4_CORE_SCALABLE             int = C.FF_PROFILE_MPEG4_CORE_SCALABLE
	FF_PROFILE_MPEG4_ADVANCED_CODING           int = C.FF_PROFILE_MPEG4_ADVANCED_CODING
	FF_PROFILE_MPEG4_ADVANCED_CORE             int = C.FF_PROFILE_MPEG4_ADVANCED_CORE
	FF_PROFILE_MPEG4_ADVANCED_SCALABLE_TEXTURE int = C.FF_PROFILE_MPEG4_ADVANCED_SCALABLE_TEXTURE
	FF_PROFILE_MPEG4_SIMPLE_STUDIO             int = C.FF_PROFILE_MPEG4_SIMPLE_STUDIO
	FF_PROFILE_MPEG4_ADVANCED_SIMPLE           int = C.FF_PROFILE_MPEG4_ADVANCED_SIMPLE

	AV_NOPTS_VALUE int64 = C.AV_NOPTS_VALUE

	FF_PROFILE_JPEG2000_CSTREAM_RESTRICTION_0  int = C.FF_PROFILE_JPEG2000_CSTREAM_RESTRICTION_0
	FF_PROFILE_JPEG2000_CSTREAM_RESTRICTION_1  int = C.FF_PROFILE_JPEG2000_CSTREAM_RESTRICTION_1
	FF_PROFILE_JPEG2000_CSTREAM_NO_RESTRICTION int = C.FF_PROFILE_JPEG2000_CSTREAM_NO_RESTRICTION
	FF_PROFILE_JPEG2000_DCINEMA_2K             int = C.FF_PROFILE_JPEG2000_DCINEMA_2K
	FF_PROFILE_JPEG2000_DCINEMA_4K             int = C.FF_PROFILE_JPEG2000_DCINEMA_4K

	FF_PROFILE_VP9_0 int = C.FF_PROFILE_VP9_0
	FF_PROFILE_VP9_1 int = C.FF_PROFILE_VP9_1
	FF_PROFILE_VP9_2 int = C.FF_PROFILE_VP9_2
	FF_PROFILE_VP9_3 int = C.FF_PROFILE_VP9_3

	FF_PROFILE_HEVC_MAIN               int = C.FF_PROFILE_HEVC_MAIN
	FF_PROFILE_HEVC_MAIN_10            int = C.FF_PROFILE_HEVC_MAIN_10
	FF_PROFILE_HEVC_MAIN_STILL_PICTURE int = C.FF_PROFILE_HEVC_MAIN_STILL_PICTURE
	FF_PROFILE_HEVC_REXT               int = C.FF_PROFILE_HEVC_REXT
)

func init() {
	C.avcodec_register_all()
	InitDesc()
}

type Codec struct {
	avCodec *C.struct_AVCodec
	CgoMemoryManage
}

func FindDecoder(i interface{}) (*Codec, error) {
	var avc *C.struct_AVCodec

	switch t := i.(type) {
	case string:
		cname := C.CString(i.(string))
		defer C.free(unsafe.Pointer(cname))

		avc = C.avcodec_find_decoder_by_name(cname)
		break

	case int:
		avc = C.avcodec_find_decoder(uint32(i.(int)))
		break

	default:
		return nil, errors.New(fmt.Sprintf("Unable to find codec, unexpected arguments type '%v'", t))
	}

	if avc == nil {
		return nil, errors.New(fmt.Sprintf("Unable to find codec by value '%v'", i))
	}

	return &Codec{avCodec: avc}, nil
}

func FindEncoder(i interface{}) (*Codec, error) {
	var avc *C.struct_AVCodec

	switch t := i.(type) {
	case string:
		cname := C.CString(i.(string))
		defer C.free(unsafe.Pointer(cname))

		avc = C.avcodec_find_encoder_by_name(cname)
		break

	case int:
		avc = C.avcodec_find_encoder(uint32(i.(int)))
		break

	default:
		return nil, errors.New(fmt.Sprintf("Unable to find codec, unexpected arguments type '%v'", t))
	}

	if avc == nil {
		return nil, errors.New(fmt.Sprintf("Unable to find codec by value '%v'", i))
	}

	return &Codec{avCodec: avc}, nil
}

func (this *Codec) Free() {
	//nothing to do
}
func (this *Codec) Id() int {
	return int(this.avCodec.id)
}

func (this *Codec) Name() string {
	return C.GoString(this.avCodec.name)
}

func (this *Codec) LongName() string {
	return C.GoString(this.avCodec.long_name)
}

func (this *Codec) Type() int {
	// > ...field names that are keywords in Go can be
	// > accessed by prefixing them with an underscore
	return int(this.avCodec._type)
}

func (this *Codec) IsExperimental() bool {
	return bool((this.avCodec.capabilities & C.CODEC_CAP_EXPERIMENTAL) != 0)
}
<|endoftext|>