<commit_before>// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package transport_test

import (
	"context"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.uber.org/yarpc"
	"go.uber.org/yarpc/encoding/json"
	"go.uber.org/yarpc/transport/http"
	ytchannel "go.uber.org/yarpc/transport/tchannel"

	"github.com/opentracing/opentracing-go"
	"github.com/opentracing/opentracing-go/mocktracer"
)

type echoReqBody struct{}
type echoResBody struct{}

type handler struct {
	client json.Client
	t      *testing.T
}

func (h handler) register(dispatcher *yarpc.Dispatcher) {
	dispatcher.Register(json.Procedure("echo", h.handleEcho))
	dispatcher.Register(json.Procedure("echoecho", h.handleEchoEcho))
}

func (h handler) handleEcho(ctx context.Context, reqBody *echoReqBody) (*echoResBody, error) {
	h.assertBaggage(ctx)
	return &echoResBody{}, nil
}

func (h handler) handleEchoEcho(ctx context.Context, reqBody *echoReqBody) (*echoResBody, error) {
	h.assertBaggage(ctx)
	var resBody echoResBody
	err := h.client.Call(ctx, "echo", reqBody, &resBody)
	return &resBody, err
}

func (h handler) echo(ctx context.Context) error {
	return h.client.Call(ctx, "echo", &echoReqBody{}, &echoResBody{})
}

func (h handler) echoEcho(ctx context.Context) error {
	return h.client.Call(ctx, "echoecho", &echoReqBody{}, &echoResBody{})
}

func (h handler) createContextWithBaggage(tracer opentracing.Tracer) (context.Context, func()) {
	ctx := context.Background()
	ctx, cancel := context.WithTimeout(ctx, time.Second)

	span := tracer.StartSpan("test")
	// no defer span.Finish()
	span.SetBaggageItem("weapon", "knife")
	ctx = opentracing.ContextWithSpan(ctx, span)

	return ctx, cancel
}

func (h handler) assertBaggage(ctx context.Context) {
	span := opentracing.SpanFromContext(ctx)
	weapon := span.BaggageItem("weapon")
	assert.Equal(h.t, "knife", weapon, "baggage should propagate")
}

func createHTTPDispatcher(tracer opentracing.Tracer) *yarpc.Dispatcher {
	// TODO: Use port 0 once https://github.com/yarpc/yarpc-go/issues/381 is
	// fixed.

	httpTransport := http.NewTransport(http.Tracer(tracer))
	dispatcher := yarpc.NewDispatcher(yarpc.Config{
		Name: "yarpc-test",
		Inbounds: yarpc.Inbounds{
			httpTransport.NewInbound(":18080"),
		},
		Outbounds: yarpc.Outbounds{
			"yarpc-test": {
				Unary: httpTransport.NewSingleOutbound("http://127.0.0.1:18080"),
			},
		},
		Tracer: tracer,
	})

	return dispatcher
}

func createTChannelDispatcher(tracer opentracing.Tracer, t *testing.T) *yarpc.Dispatcher {
	hp := "127.0.0.1:4040"

	tchannelTransport, err := ytchannel.NewChannelTransport(
		ytchannel.ListenAddr(hp),
		ytchannel.Tracer(tracer),
		ytchannel.ServiceName("yarpc-test"),
	)
	require.NoError(t, err)

	dispatcher := yarpc.NewDispatcher(yarpc.Config{
		Name: "yarpc-test",
		Inbounds: yarpc.Inbounds{
			tchannelTransport.NewInbound(),
		},
		Outbounds: yarpc.Outbounds{
			"yarpc-test": {
				Unary: tchannelTransport.NewSingleOutbound(hp),
			},
		},
		Tracer: tracer,
	})

	return dispatcher
}

func TestHTTPTracer(t *testing.T) {
	tracer := mocktracer.New()
	dispatcher := createHTTPDispatcher(tracer)

	client := json.New(dispatcher.ClientConfig("yarpc-test"))
	handler := handler{client: client, t: t}
	handler.register(dispatcher)

	require.NoError(t, dispatcher.Start())
	defer dispatcher.Stop()

	ctx, cancel := handler.createContextWithBaggage(tracer)
	defer cancel()

	err := handler.echo(ctx)
	assert.NoError(t, err)

	AssertDepth1Spans(t, tracer)
}

func TestTChannelTracer(t *testing.T) {
	tracer := mocktracer.New()
	dispatcher := createTChannelDispatcher(tracer, t)
	// Make this assertion at the end of the defer stack, when the channel has
	// been shut down. This ensures that all message exchanges have been shut
	// down, which means that all spans have been closed.
	defer AssertDepth1Spans(t, tracer)

	client := json.New(dispatcher.ClientConfig("yarpc-test"))
	handler := handler{client: client, t: t}
	handler.register(dispatcher)

	require.NoError(t, dispatcher.Start())
	defer dispatcher.Stop()

	ctx, cancel := handler.createContextWithBaggage(tracer)
	defer cancel()

	err := handler.echo(ctx)
	assert.NoError(t, err)
}

func AssertDepth1Spans(t *testing.T, tracer *mocktracer.MockTracer) {
	assert.Equal(t, 2, len(tracer.FinishedSpans()), "generates inbound and outband spans")
	if len(tracer.FinishedSpans()) != 2 {
		return
	}
	spans := tracer.FinishedSpans()
	parent := spans[0]
	child := spans[1]
	parentctx := parent.Context().(mocktracer.MockSpanContext)
	childctx := child.Context().(mocktracer.MockSpanContext)
	assert.Equal(t, parentctx.TraceID, childctx.TraceID, "parent and child trace ID do not match")
	// Whether the parent and child have the same span id is an implementation
	// detail of the tracer.
	assert.Equal(t, "echo", parent.OperationName, "span has correct operation name")
	assert.Equal(t, "echo", child.OperationName, "span has correct operation name")
}

func TestHTTPTracerDepth2(t *testing.T) {
	tracer := mocktracer.New()
	dispatcher := createHTTPDispatcher(tracer)

	client := json.New(dispatcher.ClientConfig("yarpc-test"))
	handler := handler{client: client, t: t}
	handler.register(dispatcher)

	require.NoError(t, dispatcher.Start())
	defer dispatcher.Stop()

	ctx, cancel := handler.createContextWithBaggage(tracer)
	defer cancel()

	err := handler.echoEcho(ctx)
	assert.NoError(t, err)
	AssertDepth2Spans(t, tracer)
}

func TestTChannelTracerDepth2(t *testing.T) {
	tracer := mocktracer.New()
	dispatcher := createTChannelDispatcher(tracer, t)
	// Make this assertion at the end of the defer stack, when the channel has
	// been shut down. This ensures that all message exchanges have been shut
	// down, which means that all spans have been closed.
	defer AssertDepth2Spans(t, tracer)

	client := json.New(dispatcher.ClientConfig("yarpc-test"))
	handler := handler{client: client, t: t}
	handler.register(dispatcher)

	require.NoError(t, dispatcher.Start())
	defer dispatcher.Stop()

	ctx, cancel := handler.createContextWithBaggage(tracer)
	defer cancel()

	err := handler.echoEcho(ctx)
	assert.NoError(t, err)
}

func AssertDepth2Spans(t *testing.T, tracer *mocktracer.MockTracer) {
	if !assert.Equal(t, 4, len(tracer.FinishedSpans()), "generates inbound and outband spans") {
		return
	}
	spans := tracer.FinishedSpans()
	ids := mapContexts(spans)
	assert.Equal(t, []int{ids[0], ids[0], ids[0], ids[0]}, ids, "spans share a trace id")
	assert.Equal(t, "echo", spans[0].OperationName, "span has correct operation name")
	assert.Equal(t, "echo", spans[1].OperationName, "span has correct operation name")
	assert.Equal(t, "echoecho", spans[2].OperationName, "span has correct operation name")
	assert.Equal(t, "echoecho", spans[3].OperationName, "span has correct operation name")
}

func mapContexts(spans []*mocktracer.MockSpan) []int {
	ids := make([]int, len(spans))
	for i, span := range spans {
		ids[i] = span.Context().(mocktracer.MockSpanContext).TraceID
	}
	return ids
}
<commit_msg>Skip TChannel tracer tests (#872)<commit_after>// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package transport_test

import (
	"context"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.uber.org/yarpc"
	"go.uber.org/yarpc/encoding/json"
	"go.uber.org/yarpc/transport/http"
	ytchannel "go.uber.org/yarpc/transport/tchannel"

	"github.com/opentracing/opentracing-go"
	"github.com/opentracing/opentracing-go/mocktracer"
)

type echoReqBody struct{}
type echoResBody struct{}

type handler struct {
	client json.Client
	t      *testing.T
}

func (h handler) register(dispatcher *yarpc.Dispatcher) {
	dispatcher.Register(json.Procedure("echo", h.handleEcho))
	dispatcher.Register(json.Procedure("echoecho", h.handleEchoEcho))
}

func (h handler) handleEcho(ctx context.Context, reqBody *echoReqBody) (*echoResBody, error) {
	h.assertBaggage(ctx)
	return &echoResBody{}, nil
}

func (h handler) handleEchoEcho(ctx context.Context, reqBody *echoReqBody) (*echoResBody, error) {
	h.assertBaggage(ctx)
	var resBody echoResBody
	err := h.client.Call(ctx, "echo", reqBody, &resBody)
	return &resBody, err
}

func (h handler) echo(ctx context.Context) error {
	return h.client.Call(ctx, "echo", &echoReqBody{}, &echoResBody{})
}

func (h handler) echoEcho(ctx context.Context) error {
	return h.client.Call(ctx, "echoecho", &echoReqBody{}, &echoResBody{})
}

func (h handler) createContextWithBaggage(tracer opentracing.Tracer) (context.Context, func()) {
	ctx := context.Background()
	ctx, cancel := context.WithTimeout(ctx, time.Second)

	span := tracer.StartSpan("test")
	// no defer span.Finish()
	span.SetBaggageItem("weapon", "knife")
	ctx = opentracing.ContextWithSpan(ctx, span)

	return ctx, cancel
}

func (h handler) assertBaggage(ctx context.Context) {
	span := opentracing.SpanFromContext(ctx)
	weapon := span.BaggageItem("weapon")
	assert.Equal(h.t, "knife", weapon, "baggage should propagate")
}

func createHTTPDispatcher(tracer opentracing.Tracer) *yarpc.Dispatcher {
	// TODO: Use port 0 once https://github.com/yarpc/yarpc-go/issues/381 is
	// fixed.

	httpTransport := http.NewTransport(http.Tracer(tracer))
	dispatcher := yarpc.NewDispatcher(yarpc.Config{
		Name: "yarpc-test",
		Inbounds: yarpc.Inbounds{
			httpTransport.NewInbound(":18080"),
		},
		Outbounds: yarpc.Outbounds{
			"yarpc-test": {
				Unary: httpTransport.NewSingleOutbound("http://127.0.0.1:18080"),
			},
		},
		Tracer: tracer,
	})

	return dispatcher
}

func createTChannelDispatcher(tracer opentracing.Tracer, t *testing.T) *yarpc.Dispatcher {
	hp := "127.0.0.1:4040"

	tchannelTransport, err := ytchannel.NewChannelTransport(
		ytchannel.ListenAddr(hp),
		ytchannel.Tracer(tracer),
		ytchannel.ServiceName("yarpc-test"),
	)
	require.NoError(t, err)

	dispatcher := yarpc.NewDispatcher(yarpc.Config{
		Name: "yarpc-test",
		Inbounds: yarpc.Inbounds{
			tchannelTransport.NewInbound(),
		},
		Outbounds: yarpc.Outbounds{
			"yarpc-test": {
				Unary: tchannelTransport.NewSingleOutbound(hp),
			},
		},
		Tracer: tracer,
	})

	return dispatcher
}

func TestHTTPTracer(t *testing.T) {
	tracer := mocktracer.New()
	dispatcher := createHTTPDispatcher(tracer)

	client := json.New(dispatcher.ClientConfig("yarpc-test"))
	handler := handler{client: client, t: t}
	handler.register(dispatcher)

	require.NoError(t, dispatcher.Start())
	defer dispatcher.Stop()

	ctx, cancel := handler.createContextWithBaggage(tracer)
	defer cancel()

	err := handler.echo(ctx)
	assert.NoError(t, err)

	AssertDepth1Spans(t, tracer)
}

func TestTChannelTracer(t *testing.T) {
	t.Skip("TODO this test is flaky, we need to fix")
	tracer := mocktracer.New()
	dispatcher := createTChannelDispatcher(tracer, t)
	// Make this assertion at the end of the defer stack, when the channel has
	// been shut down. This ensures that all message exchanges have been shut
	// down, which means that all spans have been closed.
	defer AssertDepth1Spans(t, tracer)

	client := json.New(dispatcher.ClientConfig("yarpc-test"))
	handler := handler{client: client, t: t}
	handler.register(dispatcher)

	require.NoError(t, dispatcher.Start())
	defer dispatcher.Stop()

	ctx, cancel := handler.createContextWithBaggage(tracer)
	defer cancel()

	err := handler.echo(ctx)
	assert.NoError(t, err)
}

func AssertDepth1Spans(t *testing.T, tracer *mocktracer.MockTracer) {
	assert.Equal(t, 2, len(tracer.FinishedSpans()), "generates inbound and outband spans")
	if len(tracer.FinishedSpans()) != 2 {
		return
	}
	spans := tracer.FinishedSpans()
	parent := spans[0]
	child := spans[1]
	parentctx := parent.Context().(mocktracer.MockSpanContext)
	childctx := child.Context().(mocktracer.MockSpanContext)
	assert.Equal(t, parentctx.TraceID, childctx.TraceID, "parent and child trace ID do not match")
	// Whether the parent and child have the same span id is an implementation
	// detail of the tracer.
	assert.Equal(t, "echo", parent.OperationName, "span has correct operation name")
	assert.Equal(t, "echo", child.OperationName, "span has correct operation name")
}

func TestHTTPTracerDepth2(t *testing.T) {
	tracer := mocktracer.New()
	dispatcher := createHTTPDispatcher(tracer)

	client := json.New(dispatcher.ClientConfig("yarpc-test"))
	handler := handler{client: client, t: t}
	handler.register(dispatcher)

	require.NoError(t, dispatcher.Start())
	defer dispatcher.Stop()

	ctx, cancel := handler.createContextWithBaggage(tracer)
	defer cancel()

	err := handler.echoEcho(ctx)
	assert.NoError(t, err)
	AssertDepth2Spans(t, tracer)
}

func TestTChannelTracerDepth2(t *testing.T) {
	t.Skip("TODO this test is flaky, we need to fix")
	tracer := mocktracer.New()
	dispatcher := createTChannelDispatcher(tracer, t)
	// Make this assertion at the end of the defer stack, when the channel has
	// been shut down. This ensures that all message exchanges have been shut
	// down, which means that all spans have been closed.
	defer AssertDepth2Spans(t, tracer)

	client := json.New(dispatcher.ClientConfig("yarpc-test"))
	handler := handler{client: client, t: t}
	handler.register(dispatcher)

	require.NoError(t, dispatcher.Start())
	defer dispatcher.Stop()

	ctx, cancel := handler.createContextWithBaggage(tracer)
	defer cancel()

	err := handler.echoEcho(ctx)
	assert.NoError(t, err)
}

func AssertDepth2Spans(t *testing.T, tracer *mocktracer.MockTracer) {
	if !assert.Equal(t, 4, len(tracer.FinishedSpans()), "generates inbound and outband spans") {
		return
	}
	spans := tracer.FinishedSpans()
	ids := mapContexts(spans)
	assert.Equal(t, []int{ids[0], ids[0], ids[0], ids[0]}, ids, "spans share a trace id")
	assert.Equal(t, "echo", spans[0].OperationName, "span has correct operation name")
	assert.Equal(t, "echo", spans[1].OperationName, "span has correct operation name")
	assert.Equal(t, "echoecho", spans[2].OperationName, "span has correct operation name")
	assert.Equal(t, "echoecho", spans[3].OperationName, "span has correct operation name")
}

func mapContexts(spans []*mocktracer.MockSpan) []int {
	ids := make([]int, len(spans))
	for i, span := range spans {
		ids[i] = span.Context().(mocktracer.MockSpanContext).TraceID
	}
	return ids
}
<|endoftext|>


<commit_before>package txtdirect

import (
	"net/http"
	"net/http/httptest"
	"testing"
)

func TestParsePlaceholders(t *testing.T) {
	tests := []struct {
		url       string
		requested string
		expected  string
	}{
		{
			"example.com{uri}",
			"https://example.com/?test=test",
			"example.com/?test=test",
		},
		{
			"example.com{uri}/{~test}",
			"https://example.com/?test=test",
			"example.com/?test=test/test",
		},
		{
			"example.com{uri}/{>Test}",
			"https://example.com/?test=test",
			"example.com/?test=test/test-header",
		},
		{
			"example.com{uri}/{?test}",
			"https://example.com/?test=test",
			"example.com/?test=test/test",
		},
		{
			"example.com{dir}",
			"https://example.com/directory/test",
			"example.com/directory/",
		},
		{
			"example.com/directory/{file}",
			"https://example.com/directory/file.pdf",
			"example.com/directory/file.pdf",
		},
		{
			"example.com/?test={fragment}",
			"https://example.com/?test=#fragment",
			"example.com/?test=fragment",
		},
		{
			"example.com/{host}",
			"https://project.example.com:8080",
			"example.com/project.example.com:8080",
		},
		{
			"example.com/{hostonly}",
			"https://project.example.com",
			"example.com/project.example.com",
		},
		{
			"example.com/{method}",
			"https://example.com",
			"example.com/GET",
		},
		{
			"example.com{path}",
			"https://example.com/path",
			"example.com/path",
		},
		{
			"example.com{path_escaped}",
			"https://example.com/path_escaped",
			"example.com%2Fpath_escaped",
		},
		{
			"subdomain.example.com/{label1}",
			"https://subdomain.example.com/subdomain",
			"subdomain.example.com/subdomain",
		},
		{
			"subdomain.example.com/{label2}",
			"https://subdomain.example.com/example",
			"subdomain.example.com/example",
		},
		{
			"subdomain.example.com/{label3}",
			"https://subdomain.example.com/com",
			"subdomain.example.com/com",
		},
	}
	for _, test := range tests {
		req := httptest.NewRequest("GET", test.requested, nil)
		req.AddCookie(&http.Cookie{Name: "test", Value: "test"})
		req.Header.Add("Test", "test-header")
		req.URL.Fragment = "fragment"
		result, err := parsePlaceholders(test.url, req)
		if err != nil {
			t.Fatal(err)
		}
		if result != test.expected {
			t.Errorf("Expected %s, got %s", test.expected, result)
		}
	}
}

func TestParsePlaceholdersFails(t *testing.T) {
	tests := []struct {
		url       string
		requested string
	}{
		{
			"example.com/{label0}",
			"https://example.com/test",
		},
		{
			"example.com/{label9000}",
			"https://example.com/test",
		},
	}
	for _, test := range tests {
		req := httptest.NewRequest("GET", test.requested, nil)
		_, err := parsePlaceholders(test.url, req)
		if err == nil {
			t.Errorf("Expected error, got nil")
		}
	}
}
<commit_msg>Add {port} placeholder test<commit_after>package txtdirect

import (
	"net/http"
	"net/http/httptest"
	"testing"
)

func TestParsePlaceholders(t *testing.T) {
	tests := []struct {
		url       string
		requested string
		expected  string
	}{
		{
			"example.com{uri}",
			"https://example.com/?test=test",
			"example.com/?test=test",
		},
		{
			"example.com{uri}/{~test}",
			"https://example.com/?test=test",
			"example.com/?test=test/test",
		},
		{
			"example.com{uri}/{>Test}",
			"https://example.com/?test=test",
			"example.com/?test=test/test-header",
		},
		{
			"example.com{uri}/{?test}",
			"https://example.com/?test=test",
			"example.com/?test=test/test",
		},
		{
			"example.com{dir}",
			"https://example.com/directory/test",
			"example.com/directory/",
		},
		{
			"example.com/directory/{file}",
			"https://example.com/directory/file.pdf",
			"example.com/directory/file.pdf",
		},
		{
			"example.com/?test={fragment}",
			"https://example.com/?test=#fragment",
			"example.com/?test=fragment",
		},
		{
			"example.com/{host}",
			"https://project.example.com:8080",
			"example.com/project.example.com:8080",
		},
		{
			"example.com/{hostonly}",
			"https://project.example.com",
			"example.com/project.example.com",
		},
		{
			"example.com/{method}",
			"https://example.com",
			"example.com/GET",
		},
		{
			"example.com{path}",
			"https://example.com/path",
			"example.com/path",
		},
		{
			"example.com{path_escaped}",
			"https://example.com/path_escaped",
			"example.com%2Fpath_escaped",
		},
		{
			"example.com:{port}",
			"https://example.com:8080",
			"example.com:8080",
		},
		{
			"subdomain.example.com/{label1}",
			"https://subdomain.example.com/subdomain",
			"subdomain.example.com/subdomain",
		},
		{
			"subdomain.example.com/{label2}",
			"https://subdomain.example.com/example",
			"subdomain.example.com/example",
		},
		{
			"subdomain.example.com/{label3}",
			"https://subdomain.example.com/com",
			"subdomain.example.com/com",
		},
	}
	for _, test := range tests {
		req := httptest.NewRequest("GET", test.requested, nil)
		req.AddCookie(&http.Cookie{Name: "test", Value: "test"})
		req.Header.Add("Test", "test-header")
		req.URL.Fragment = "fragment"
		result, err := parsePlaceholders(test.url, req)
		if err != nil {
			t.Fatal(err)
		}
		if result != test.expected {
			t.Errorf("Expected %s, got %s", test.expected, result)
		}
	}
}

func TestParsePlaceholdersFails(t *testing.T) {
	tests := []struct {
		url       string
		requested string
	}{
		{
			"example.com/{label0}",
			"https://example.com/test",
		},
		{
			"example.com/{label9000}",
			"https://example.com/test",
		},
	}
	for _, test := range tests {
		req := httptest.NewRequest("GET", test.requested, nil)
		_, err := parsePlaceholders(test.url, req)
		if err == nil {
			t.Errorf("Expected error, got nil")
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 Jacques Supcik <jacques.supcik@hefr.ch>
//
// 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.

//   _   _                     _                             _        _
//  | |_| |__  _   _ _ __ ___ (_) ___         ___ __ _ _ __ | |_ __ _(_)_ __
//  | __| '_ \| | | | '_ ` _ \| |/ _ \ _____ / __/ _` | '_ \| __/ _` | | '_ \
//  | |_| | | | |_| | | | | | | | (_) |_____| (_| (_| | |_) | || (_| | | | | |
//   \__|_| |_|\__, |_| |_| |_|_|\___/       \___\__,_| .__/ \__\__,_|_|_| |_|
//             |___/                                  |_|
//

// Frontend
package main

import (
	"flag"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
	"github.com/kidstuff/mongostore"
	"gopkg.in/mgo.v2"
	"html/template"
	"net/http"
)

const (
	dbName     = "thymio_captain"
	sessionC   = "sessions"
	maxAge     = 24 * 3600
	sessionKey = "session-key"
	root       = "internal_pages"
)

var (
	database *mgo.Session
	store    *mongostore.MongoStore
)

func initSession(w http.ResponseWriter, r *http.Request) (vars map[string]string, session *sessions.Session, err error) {
	database.Refresh()
	vars = mux.Vars(r)
	session, err = store.Get(r, sessionKey)
	if err != nil {
		log.Error(err.Error())
		http.Error(w, "Session Error", 500)
	} else {
		w.Header().Set("Cache-Control", "max-age=0, no-cache, no-store")
		w.Header().Set("Pragma", "no-cache")
	}
	return
}

func CardLogin(w http.ResponseWriter, r *http.Request) {
	vars, session, err := initSession(w, r)
	if err != nil {
		return
	}

	if vars["CardId"] == "friendship" {
		session.Values["admin"] = "1"
		sessions.Save(r, w)
		http.ServeFile(w, r, root+"/login-ok.html")
	} else {
		session.Values["admin"] = "0"
		sessions.Save(r, w)
		http.ServeFile(w, r, root+"/login-failed.html")
	}
}

func Logout(w http.ResponseWriter, r *http.Request) {
	_, session, err := initSession(w, r)
	if err != nil {
		return
	}

	session.Values["admin"] = "0"
	sessions.Save(r, w)
	http.ServeFile(w, r, root+"/logout.html")
}

func Debug(w http.ResponseWriter, r *http.Request) {
	_, session, err := initSession(w, r)
	if err != nil {
		return
	}

	tmpl, err := template.ParseFiles(root + "/debug.html")
	if err != nil {
		log.Infof("Error 1 %s", err)
	}
	s := fmt.Sprintf("%v", session.Values)
	if err != nil {
		log.Infof("Error 2 %s", err)
	}
	err = tmpl.Execute(w, struct{ Session string }{string(s)})
}

func Start(w http.ResponseWriter, r *http.Request) {
	vars, session, err := initSession(w, r)
	if err != nil {
		return
	}

	session.Values["cardId"] = vars["CardId"]
	sessions.Save(r, w)

	if session.Values["admin"] == "1" {
		http.ServeFile(w, r, root+"/admin.html")
	} else {
		http.ServeFile(w, r, root+"/public.html")
	}
}

func main() {
	var port = flag.Int("port", 8080, "port")
	var debug = flag.Bool("debug", false, "run in debug mode")
	var domain = flag.String("domain", "thymio.tk", "Domain name (for the cookie)")
	var mongoServer = flag.String("mongo-server", "localhost", "MongoDB server URL")
	var secretKey = flag.String("secret-key", "not-so-secret", "Secret key (for secure cookies)")

	flag.Parse()

	if *debug {
		log.SetLevel(log.DebugLevel)
		log.Debug("Debug mode")
	} else {
		log.SetLevel(log.InfoLevel)
	}

	var err error
	database, err = mgo.Dial(*mongoServer)
	if err != nil {
		log.Fatal(err)
	}
	store = mongostore.NewMongoStore(
		database.DB(dbName).C(sessionC),
		maxAge, true, []byte(*secretKey))

	store.Options.Domain = *domain

	r := mux.NewRouter()
	r.HandleFunc("/start/{CardId}", Start)
	r.HandleFunc("/cardlogin/{CardId}", CardLogin)
	r.HandleFunc("/logout", Logout)
	r.HandleFunc("/debug", Debug)

	http.Handle("/", r)
	log.Infof("Ready, listening on port %d", *port)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *port), nil))
}
<commit_msg>Add hmac card id validation<commit_after>// Copyright 2016 Jacques Supcik <jacques.supcik@hefr.ch>
//
// 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.

//   _   _                     _                             _        _
//  | |_| |__  _   _ _ __ ___ (_) ___         ___ __ _ _ __ | |_ __ _(_)_ __
//  | __| '_ \| | | | '_ ` _ \| |/ _ \ _____ / __/ _` | '_ \| __/ _` | | '_ \
//  | |_| | | | |_| | | | | | | | (_) |_____| (_| (_| | |_) | || (_| | | | | |
//   \__|_| |_|\__, |_| |_| |_|_|\___/       \___\__,_| .__/ \__\__,_|_|_| |_|
//             |___/                                  |_|
//

// Frontend
package main

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/hex"
	"flag"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/gorilla/mux"
	"github.com/gorilla/sessions"
	"github.com/kidstuff/mongostore"
	"gopkg.in/mgo.v2"
	"html/template"
	"net/http"
)

const (
	dbName     = "thymio_captain"
	sessionC   = "sessions"
	maxAge     = 24 * 3600
	sessionKey = "session-key"
	root       = "internal_pages"
)

var (
	database       *mgo.Session
	store          *mongostore.MongoStore
	adminSecretKey *string
	startSecretKey *string
)

func initSession(w http.ResponseWriter, r *http.Request) (vars map[string]string, session *sessions.Session, err error) {
	database.Refresh()
	vars = mux.Vars(r)
	session, err = store.Get(r, sessionKey)
	if err != nil {
		log.Error(err.Error())
		http.Error(w, "Session Error", 500)
	} else {
		w.Header().Set("Cache-Control", "max-age=0, no-cache, no-store")
		w.Header().Set("Pragma", "no-cache")
	}
	return
}

func isValidToken(token string, key string) bool {
	if len(token) != 40 {
		return false
	}
	data := token[0:20]
	sig := token[20:40]
	mac := hmac.New(sha1.New, []byte(key))
	mac.Write([]byte(data))
	s, err := hex.DecodeString(sig)
	return err == nil && hmac.Equal(mac.Sum(nil), s)
}

func CardLogin(w http.ResponseWriter, r *http.Request) {
	vars, session, err := initSession(w, r)
	if err != nil {
		return
	}

	if isValidToken(vars["CardId"], *adminSecretKey) {
		session.Values["admin"] = "1"
		sessions.Save(r, w)
		http.ServeFile(w, r, root+"/login-ok.html")
	} else {
		session.Values["admin"] = "0"
		sessions.Save(r, w)
		http.ServeFile(w, r, root+"/login-failed.html")
	}
}

func Logout(w http.ResponseWriter, r *http.Request) {
	_, session, err := initSession(w, r)
	if err != nil {
		return
	}

	session.Values["admin"] = "0"
	sessions.Save(r, w)
	http.ServeFile(w, r, root+"/logout.html")
}

func Debug(w http.ResponseWriter, r *http.Request) {
	_, session, err := initSession(w, r)
	if err != nil {
		return
	}

	tmpl, err := template.ParseFiles(root + "/debug.html")
	if err != nil {
		log.Infof("Error 1 %s", err)
	}
	s := fmt.Sprintf("%v", session.Values)
	if err != nil {
		log.Infof("Error 2 %s", err)
	}
	err = tmpl.Execute(w, struct{ Session string }{string(s)})
}

func Start(w http.ResponseWriter, r *http.Request) {
	vars, session, err := initSession(w, r)
	if err != nil {
		return
	}

	if *startSecretKey == "" || isValidToken(vars["CardId"], *startSecretKey) {
		session.Values["cardId"] = vars["CardId"]
		sessions.Save(r, w)

		if session.Values["admin"] == "1" {
			http.ServeFile(w, r, root+"/admin.html")
		} else {
			http.ServeFile(w, r, root+"/public.html")
		}
	} else {
		http.ServeFile(w, r, root+"/invalid-id.html")
	}
}

func main() {
	var port = flag.Int("port", 8080, "port")
	var debug = flag.Bool("debug", false, "run in debug mode")
	var domain = flag.String("domain", "thymio.tk", "Domain name (for the cookie)")
	var mongoServer = flag.String("mongo-server", "localhost", "MongoDB server URL")
	var cookieSecretKey = flag.String("cookie-secret-key", "not-so-secret", "Secret key (for secure cookies)")
	adminSecretKey = flag.String("admin-secret-key", "change-me", "Secret key (for admin card-login)")
	startSecretKey = flag.String("start-secret-key", "", "Secret key (for start ID)")

	flag.Parse()

	if *debug {
		log.SetLevel(log.DebugLevel)
		log.Debug("Debug mode")
	} else {
		log.SetLevel(log.InfoLevel)
	}

	var err error
	database, err = mgo.Dial(*mongoServer)
	if err != nil {
		log.Fatal(err)
	}
	store = mongostore.NewMongoStore(
		database.DB(dbName).C(sessionC),
		maxAge, true, []byte(*cookieSecretKey))

	store.Options.Domain = *domain

	r := mux.NewRouter()
	r.HandleFunc("/start/{CardId}", Start)
	r.HandleFunc("/cardlogin/{CardId}", CardLogin)
	r.HandleFunc("/logout", Logout)
	r.HandleFunc("/debug", Debug)

	http.Handle("/", r)
	log.Infof("Ready, listening on port %d", *port)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *port), nil))
}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes Authors All rights reserved.

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

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

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

package api

import (
	"k8s.io/kubernetes/pkg/api/resource"
)

// Returns string version of ResourceName.
func (self ResourceName) String() string {
	return string(self)
}

// Returns the CPU limit if specified.
func (self *ResourceList) Cpu() *resource.Quantity {
	if val, ok := (*self)[ResourceCPU]; ok {
		return &val
	}
	return &resource.Quantity{}
}

// Returns the Memory limit if specified.
func (self *ResourceList) Memory() *resource.Quantity {
	if val, ok := (*self)[ResourceMemory]; ok {
		return &val
	}
	return &resource.Quantity{}
}

func (self *ResourceList) Pods() *resource.Quantity {
	if val, ok := (*self)[ResourcePods]; ok {
		return &val
	}
	return &resource.Quantity{}
}

func GetContainerStatus(statuses []ContainerStatus, name string) (ContainerStatus, bool) {
	for i := range statuses {
		if statuses[i].Name == name {
			return statuses[i], true
		}
	}
	return ContainerStatus{}, false
}

func GetExistingContainerStatus(statuses []ContainerStatus, name string) ContainerStatus {
	for i := range statuses {
		if statuses[i].Name == name {
			return statuses[i]
		}
	}
	return ContainerStatus{}
}

// IsPodReady retruns true if a pod is ready; false otherwise.
func IsPodReady(pod *Pod) bool {
	return IsPodReadyConditionTrue(pod.Status)
}

// IsPodReady retruns true if a pod is ready; false otherwise.
func IsPodReadyConditionTrue(status PodStatus) bool {
	condition := GetPodReadyCondition(status)
	return condition != nil && condition.Status == ConditionTrue
}

// Extracts the pod ready condition from the given status and returns that.
// Returns nil if the condition is not present.
func GetPodReadyCondition(status PodStatus) *PodCondition {
	for _, c := range status.Conditions {
		if c.Type == PodReady {
			return &c
		}
	}
	return nil
}
<commit_msg>Setting PodReadyCondition.LastTransitionTime<commit_after>/*
Copyright 2014 The Kubernetes Authors All rights reserved.

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

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

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

package api

import (
	"k8s.io/kubernetes/pkg/api/resource"
)

// Returns string version of ResourceName.
func (self ResourceName) String() string {
	return string(self)
}

// Returns the CPU limit if specified.
func (self *ResourceList) Cpu() *resource.Quantity {
	if val, ok := (*self)[ResourceCPU]; ok {
		return &val
	}
	return &resource.Quantity{}
}

// Returns the Memory limit if specified.
func (self *ResourceList) Memory() *resource.Quantity {
	if val, ok := (*self)[ResourceMemory]; ok {
		return &val
	}
	return &resource.Quantity{}
}

func (self *ResourceList) Pods() *resource.Quantity {
	if val, ok := (*self)[ResourcePods]; ok {
		return &val
	}
	return &resource.Quantity{}
}

func GetContainerStatus(statuses []ContainerStatus, name string) (ContainerStatus, bool) {
	for i := range statuses {
		if statuses[i].Name == name {
			return statuses[i], true
		}
	}
	return ContainerStatus{}, false
}

func GetExistingContainerStatus(statuses []ContainerStatus, name string) ContainerStatus {
	for i := range statuses {
		if statuses[i].Name == name {
			return statuses[i]
		}
	}
	return ContainerStatus{}
}

// IsPodReady retruns true if a pod is ready; false otherwise.
func IsPodReady(pod *Pod) bool {
	return IsPodReadyConditionTrue(pod.Status)
}

// IsPodReady retruns true if a pod is ready; false otherwise.
func IsPodReadyConditionTrue(status PodStatus) bool {
	condition := GetPodReadyCondition(status)
	return condition != nil && condition.Status == ConditionTrue
}

// Extracts the pod ready condition from the given status and returns that.
// Returns nil if the condition is not present.
func GetPodReadyCondition(status PodStatus) *PodCondition {
	for i, c := range status.Conditions {
		if c.Type == PodReady {
			return &status.Conditions[i]
		}
	}
	return nil
}
<|endoftext|>


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

import (
	"encoding/json"
	"fmt"

	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/selection"
	"k8s.io/apimachinery/pkg/types"
)

// LabelSelectorAsSelector converts the LabelSelector api type into a struct that implements
// labels.Selector
// Note: This function should be kept in sync with the selector methods in pkg/labels/selector.go
func LabelSelectorAsSelector(ps *LabelSelector) (labels.Selector, error) {
	if ps == nil {
		return labels.Nothing(), nil
	}
	if len(ps.MatchLabels)+len(ps.MatchExpressions) == 0 {
		return labels.Everything(), nil
	}
	selector := labels.NewSelector()
	for k, v := range ps.MatchLabels {
		r, err := labels.NewRequirement(k, selection.Equals, []string{v})
		if err != nil {
			return nil, err
		}
		selector = selector.Add(*r)
	}
	for _, expr := range ps.MatchExpressions {
		var op selection.Operator
		switch expr.Operator {
		case LabelSelectorOpIn:
			op = selection.In
		case LabelSelectorOpNotIn:
			op = selection.NotIn
		case LabelSelectorOpExists:
			op = selection.Exists
		case LabelSelectorOpDoesNotExist:
			op = selection.DoesNotExist
		default:
			return nil, fmt.Errorf("%q is not a valid pod selector operator", expr.Operator)
		}
		r, err := labels.NewRequirement(expr.Key, op, append([]string(nil), expr.Values...))
		if err != nil {
			return nil, err
		}
		selector = selector.Add(*r)
	}
	return selector, nil
}

// LabelSelectorAsMap converts the LabelSelector api type into a map of strings, ie. the
// original structure of a label selector. Operators that cannot be converted into plain
// labels (Exists, DoesNotExist, NotIn, and In with more than one value) will result in
// an error.
func LabelSelectorAsMap(ps *LabelSelector) (map[string]string, error) {
	if ps == nil {
		return nil, nil
	}
	selector := map[string]string{}
	for k, v := range ps.MatchLabels {
		selector[k] = v
	}
	for _, expr := range ps.MatchExpressions {
		switch expr.Operator {
		case LabelSelectorOpIn:
			if len(expr.Values) != 1 {
				return selector, fmt.Errorf("operator %q without a single value cannot be converted into the old label selector format", expr.Operator)
			}
			// Should we do anything in case this will override a previous key-value pair?
			selector[expr.Key] = expr.Values[0]
		case LabelSelectorOpNotIn, LabelSelectorOpExists, LabelSelectorOpDoesNotExist:
			return selector, fmt.Errorf("operator %q cannot be converted into the old label selector format", expr.Operator)
		default:
			return selector, fmt.Errorf("%q is not a valid selector operator", expr.Operator)
		}
	}
	return selector, nil
}

// ParseToLabelSelector parses a string representing a selector into a LabelSelector object.
// Note: This function should be kept in sync with the parser in pkg/labels/selector.go
func ParseToLabelSelector(selector string) (*LabelSelector, error) {
	reqs, err := labels.ParseToRequirements(selector)
	if err != nil {
		return nil, fmt.Errorf("couldn't parse the selector string \"%s\": %v", selector, err)
	}

	labelSelector := &LabelSelector{
		MatchLabels:      map[string]string{},
		MatchExpressions: []LabelSelectorRequirement{},
	}
	for _, req := range reqs {
		var op LabelSelectorOperator
		switch req.Operator() {
		case selection.Equals, selection.DoubleEquals:
			vals := req.Values()
			if vals.Len() != 1 {
				return nil, fmt.Errorf("equals operator must have exactly one value")
			}
			val, ok := vals.PopAny()
			if !ok {
				return nil, fmt.Errorf("equals operator has exactly one value but it cannot be retrieved")
			}
			labelSelector.MatchLabels[req.Key()] = val
			continue
		case selection.In:
			op = LabelSelectorOpIn
		case selection.NotIn:
			op = LabelSelectorOpNotIn
		case selection.Exists:
			op = LabelSelectorOpExists
		case selection.DoesNotExist:
			op = LabelSelectorOpDoesNotExist
		case selection.GreaterThan, selection.LessThan:
			// Adding a separate case for these operators to indicate that this is deliberate
			return nil, fmt.Errorf("%q isn't supported in label selectors", req.Operator())
		default:
			return nil, fmt.Errorf("%q is not a valid label selector operator", req.Operator())
		}
		labelSelector.MatchExpressions = append(labelSelector.MatchExpressions, LabelSelectorRequirement{
			Key:      req.Key(),
			Operator: op,
			Values:   req.Values().List(),
		})
	}
	return labelSelector, nil
}

// SetAsLabelSelector converts the labels.Set object into a LabelSelector api object.
func SetAsLabelSelector(ls labels.Set) *LabelSelector {
	if ls == nil {
		return nil
	}

	selector := &LabelSelector{
		MatchLabels: make(map[string]string),
	}
	for label, value := range ls {
		selector.MatchLabels[label] = value
	}

	return selector
}

// FormatLabelSelector convert labelSelector into plain string
func FormatLabelSelector(labelSelector *LabelSelector) string {
	selector, err := LabelSelectorAsSelector(labelSelector)
	if err != nil {
		return "<error>"
	}

	l := selector.String()
	if len(l) == 0 {
		l = "<none>"
	}
	return l
}

func ExtractGroupVersions(l *APIGroupList) []string {
	var groupVersions []string
	for _, g := range l.Groups {
		for _, gv := range g.Versions {
			groupVersions = append(groupVersions, gv.GroupVersion)
		}
	}
	return groupVersions
}

// HasAnnotation returns a bool if passed in annotation exists
func HasAnnotation(obj ObjectMeta, ann string) bool {
	_, found := obj.Annotations[ann]
	return found
}

// SetMetaDataAnnotation sets the annotation and value
func SetMetaDataAnnotation(obj *ObjectMeta, ann string, value string) {
	if obj.Annotations == nil {
		obj.Annotations = make(map[string]string)
	}
	obj.Annotations[ann] = value
}

// SingleObject returns a ListOptions for watching a single object.
func SingleObject(meta ObjectMeta) ListOptions {
	return ListOptions{
		FieldSelector:   fields.OneTermEqualSelector("metadata.name", meta.Name).String(),
		ResourceVersion: meta.ResourceVersion,
	}
}

// NewDeleteOptions returns a DeleteOptions indicating the resource should
// be deleted within the specified grace period. Use zero to indicate
// immediate deletion. If you would prefer to use the default grace period,
// use &metav1.DeleteOptions{} directly.
func NewDeleteOptions(grace int64) *DeleteOptions {
	return &DeleteOptions{GracePeriodSeconds: &grace}
}

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

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

// NewPreconditionDeleteOptionsWithResourceVersion returns a DeleteOptions with a UID precondition set.
func NewPreconditionDeleteOptionsWithResourceVersion(rv string) *DeleteOptions {
	p := Preconditions{ResourceVersion: &rv}
	return &DeleteOptions{Preconditions: &p}
}

// HasObjectMetaSystemFieldValues returns true if fields that are managed by the system on ObjectMeta have values.
func HasObjectMetaSystemFieldValues(meta Object) bool {
	return !meta.GetCreationTimestamp().Time.IsZero() ||
		len(meta.GetUID()) != 0
}

// ResetObjectMetaForStatus forces the meta fields for a status update to match the meta fields
// for a pre-existing object. This is opt-in for new objects with Status subresource.
func ResetObjectMetaForStatus(meta, existingMeta Object) {
	meta.SetDeletionTimestamp(existingMeta.GetDeletionTimestamp())
	meta.SetGeneration(existingMeta.GetGeneration())
	meta.SetSelfLink(existingMeta.GetSelfLink())
	meta.SetLabels(existingMeta.GetLabels())
	meta.SetAnnotations(existingMeta.GetAnnotations())
	meta.SetFinalizers(existingMeta.GetFinalizers())
	meta.SetOwnerReferences(existingMeta.GetOwnerReferences())
	meta.SetManagedFields(existingMeta.GetManagedFields())
}

// MarshalJSON implements json.Marshaler
func (f Fields) MarshalJSON() ([]byte, error) {
	return json.Marshal(&f.Map)
}

// UnmarshalJSON implements json.Unmarshaler
func (f *Fields) UnmarshalJSON(b []byte) error {
	return json.Unmarshal(b, &f.Map)
}

var _ json.Marshaler = Fields{}
var _ json.Unmarshaler = &Fields{}
<commit_msg>check for ResourceVersion conflict in separate if block<commit_after>/*
Copyright 2016 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 v1

import (
	"encoding/json"
	"fmt"

	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/selection"
	"k8s.io/apimachinery/pkg/types"
)

// LabelSelectorAsSelector converts the LabelSelector api type into a struct that implements
// labels.Selector
// Note: This function should be kept in sync with the selector methods in pkg/labels/selector.go
func LabelSelectorAsSelector(ps *LabelSelector) (labels.Selector, error) {
	if ps == nil {
		return labels.Nothing(), nil
	}
	if len(ps.MatchLabels)+len(ps.MatchExpressions) == 0 {
		return labels.Everything(), nil
	}
	selector := labels.NewSelector()
	for k, v := range ps.MatchLabels {
		r, err := labels.NewRequirement(k, selection.Equals, []string{v})
		if err != nil {
			return nil, err
		}
		selector = selector.Add(*r)
	}
	for _, expr := range ps.MatchExpressions {
		var op selection.Operator
		switch expr.Operator {
		case LabelSelectorOpIn:
			op = selection.In
		case LabelSelectorOpNotIn:
			op = selection.NotIn
		case LabelSelectorOpExists:
			op = selection.Exists
		case LabelSelectorOpDoesNotExist:
			op = selection.DoesNotExist
		default:
			return nil, fmt.Errorf("%q is not a valid pod selector operator", expr.Operator)
		}
		r, err := labels.NewRequirement(expr.Key, op, append([]string(nil), expr.Values...))
		if err != nil {
			return nil, err
		}
		selector = selector.Add(*r)
	}
	return selector, nil
}

// LabelSelectorAsMap converts the LabelSelector api type into a map of strings, ie. the
// original structure of a label selector. Operators that cannot be converted into plain
// labels (Exists, DoesNotExist, NotIn, and In with more than one value) will result in
// an error.
func LabelSelectorAsMap(ps *LabelSelector) (map[string]string, error) {
	if ps == nil {
		return nil, nil
	}
	selector := map[string]string{}
	for k, v := range ps.MatchLabels {
		selector[k] = v
	}
	for _, expr := range ps.MatchExpressions {
		switch expr.Operator {
		case LabelSelectorOpIn:
			if len(expr.Values) != 1 {
				return selector, fmt.Errorf("operator %q without a single value cannot be converted into the old label selector format", expr.Operator)
			}
			// Should we do anything in case this will override a previous key-value pair?
			selector[expr.Key] = expr.Values[0]
		case LabelSelectorOpNotIn, LabelSelectorOpExists, LabelSelectorOpDoesNotExist:
			return selector, fmt.Errorf("operator %q cannot be converted into the old label selector format", expr.Operator)
		default:
			return selector, fmt.Errorf("%q is not a valid selector operator", expr.Operator)
		}
	}
	return selector, nil
}

// ParseToLabelSelector parses a string representing a selector into a LabelSelector object.
// Note: This function should be kept in sync with the parser in pkg/labels/selector.go
func ParseToLabelSelector(selector string) (*LabelSelector, error) {
	reqs, err := labels.ParseToRequirements(selector)
	if err != nil {
		return nil, fmt.Errorf("couldn't parse the selector string \"%s\": %v", selector, err)
	}

	labelSelector := &LabelSelector{
		MatchLabels:      map[string]string{},
		MatchExpressions: []LabelSelectorRequirement{},
	}
	for _, req := range reqs {
		var op LabelSelectorOperator
		switch req.Operator() {
		case selection.Equals, selection.DoubleEquals:
			vals := req.Values()
			if vals.Len() != 1 {
				return nil, fmt.Errorf("equals operator must have exactly one value")
			}
			val, ok := vals.PopAny()
			if !ok {
				return nil, fmt.Errorf("equals operator has exactly one value but it cannot be retrieved")
			}
			labelSelector.MatchLabels[req.Key()] = val
			continue
		case selection.In:
			op = LabelSelectorOpIn
		case selection.NotIn:
			op = LabelSelectorOpNotIn
		case selection.Exists:
			op = LabelSelectorOpExists
		case selection.DoesNotExist:
			op = LabelSelectorOpDoesNotExist
		case selection.GreaterThan, selection.LessThan:
			// Adding a separate case for these operators to indicate that this is deliberate
			return nil, fmt.Errorf("%q isn't supported in label selectors", req.Operator())
		default:
			return nil, fmt.Errorf("%q is not a valid label selector operator", req.Operator())
		}
		labelSelector.MatchExpressions = append(labelSelector.MatchExpressions, LabelSelectorRequirement{
			Key:      req.Key(),
			Operator: op,
			Values:   req.Values().List(),
		})
	}
	return labelSelector, nil
}

// SetAsLabelSelector converts the labels.Set object into a LabelSelector api object.
func SetAsLabelSelector(ls labels.Set) *LabelSelector {
	if ls == nil {
		return nil
	}

	selector := &LabelSelector{
		MatchLabels: make(map[string]string),
	}
	for label, value := range ls {
		selector.MatchLabels[label] = value
	}

	return selector
}

// FormatLabelSelector convert labelSelector into plain string
func FormatLabelSelector(labelSelector *LabelSelector) string {
	selector, err := LabelSelectorAsSelector(labelSelector)
	if err != nil {
		return "<error>"
	}

	l := selector.String()
	if len(l) == 0 {
		l = "<none>"
	}
	return l
}

func ExtractGroupVersions(l *APIGroupList) []string {
	var groupVersions []string
	for _, g := range l.Groups {
		for _, gv := range g.Versions {
			groupVersions = append(groupVersions, gv.GroupVersion)
		}
	}
	return groupVersions
}

// HasAnnotation returns a bool if passed in annotation exists
func HasAnnotation(obj ObjectMeta, ann string) bool {
	_, found := obj.Annotations[ann]
	return found
}

// SetMetaDataAnnotation sets the annotation and value
func SetMetaDataAnnotation(obj *ObjectMeta, ann string, value string) {
	if obj.Annotations == nil {
		obj.Annotations = make(map[string]string)
	}
	obj.Annotations[ann] = value
}

// SingleObject returns a ListOptions for watching a single object.
func SingleObject(meta ObjectMeta) ListOptions {
	return ListOptions{
		FieldSelector:   fields.OneTermEqualSelector("metadata.name", meta.Name).String(),
		ResourceVersion: meta.ResourceVersion,
	}
}

// NewDeleteOptions returns a DeleteOptions indicating the resource should
// be deleted within the specified grace period. Use zero to indicate
// immediate deletion. If you would prefer to use the default grace period,
// use &metav1.DeleteOptions{} directly.
func NewDeleteOptions(grace int64) *DeleteOptions {
	return &DeleteOptions{GracePeriodSeconds: &grace}
}

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

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

// NewPreconditionDeleteOptionsWithResourceVersion returns a DeleteOptions with a ResourceVersion precondition set.
func NewPreconditionDeleteOptionsWithResourceVersion(rv string) *DeleteOptions {
	p := Preconditions{ResourceVersion: &rv}
	return &DeleteOptions{Preconditions: &p}
}

// HasObjectMetaSystemFieldValues returns true if fields that are managed by the system on ObjectMeta have values.
func HasObjectMetaSystemFieldValues(meta Object) bool {
	return !meta.GetCreationTimestamp().Time.IsZero() ||
		len(meta.GetUID()) != 0
}

// ResetObjectMetaForStatus forces the meta fields for a status update to match the meta fields
// for a pre-existing object. This is opt-in for new objects with Status subresource.
func ResetObjectMetaForStatus(meta, existingMeta Object) {
	meta.SetDeletionTimestamp(existingMeta.GetDeletionTimestamp())
	meta.SetGeneration(existingMeta.GetGeneration())
	meta.SetSelfLink(existingMeta.GetSelfLink())
	meta.SetLabels(existingMeta.GetLabels())
	meta.SetAnnotations(existingMeta.GetAnnotations())
	meta.SetFinalizers(existingMeta.GetFinalizers())
	meta.SetOwnerReferences(existingMeta.GetOwnerReferences())
	meta.SetManagedFields(existingMeta.GetManagedFields())
}

// MarshalJSON implements json.Marshaler
func (f Fields) MarshalJSON() ([]byte, error) {
	return json.Marshal(&f.Map)
}

// UnmarshalJSON implements json.Unmarshaler
func (f *Fields) UnmarshalJSON(b []byte) error {
	return json.Unmarshal(b, &f.Map)
}

var _ json.Marshaler = Fields{}
var _ json.Unmarshaler = &Fields{}
<|endoftext|>


<commit_before>/*
Copyright 2011 Google Inc.

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 blobserver

import (
	"errors"
	"io"
	"net/http"
	"os"
	"time"

	"camlistore.org/pkg/blob"
	"camlistore.org/pkg/constants"
	"camlistore.org/pkg/context"
)

// MaxBlobSize is the size of a single blob in Camlistore.
const MaxBlobSize = constants.MaxBlobSize

var ErrCorruptBlob = errors.New("corrupt blob; digest doesn't match")

// ErrNotImplemented should be returned in methods where the function is not implemented
var ErrNotImplemented = errors.New("not implemented")

// BlobReceiver is the interface for receiving
type BlobReceiver interface {
	// ReceiveBlob accepts a newly uploaded blob and writes it to
	// permanent storage.
	//
	// Implementations of BlobReceiver downstream of the HTTP
	// server can trust that the source isn't larger than
	// MaxBlobSize and that its digest matches the provided blob
	// ref. (If not, the read of the source will fail before EOF)
	//
	// To ensure those guarantees, callers of ReceiveBlob should
	// not call ReceiveBlob directly but instead use either
	// blobserver.Receive or blobserver.ReceiveString, which also
	// take care of notifying the BlobReceiver's "BlobHub"
	// notification bus for observers.
	ReceiveBlob(br blob.Ref, source io.Reader) (blob.SizedRef, error)
}

type BlobStatter interface {
	// Stat checks for the existence of blobs, writing their sizes
	// (if found back to the dest channel), and returning an error
	// or nil.  Stat() should NOT close the channel.
	// TODO(bradfitz): redefine this to close the channel? Or document
	// better what the synchronization rules are.
	StatBlobs(dest chan<- blob.SizedRef, blobs []blob.Ref) error
}

func StatBlob(bs BlobStatter, br blob.Ref) (sb blob.SizedRef, err error) {
	c := make(chan blob.SizedRef, 1)
	err = bs.StatBlobs(c, []blob.Ref{br})
	if err != nil {
		return
	}
	select {
	case sb = <-c:
	default:
		err = os.ErrNotExist
	}
	return
}

type StatReceiver interface {
	BlobReceiver
	BlobStatter
}

type BlobEnumerator interface {
	// EnumerateBobs sends at most limit SizedBlobRef into dest,
	// sorted, as long as they are lexigraphically greater than
	// after (if provided).
	// limit will be supplied and sanity checked by caller.
	// EnumerateBlobs must close the channel.  (even if limit
	// was hit and more blobs remain, or an error is returned, or
	// the ctx is canceled)
	EnumerateBlobs(ctx *context.Context,
		dest chan<- blob.SizedRef,
		after string,
		limit int) error

	// TODO: remove limit from this interface, since the caller
	// can cancel? see if that would simplify implementations and
	// callers.
}

// BlobAndToken is the value used by the BlobStreamer interface,
// containing both a Blob and a continuation token.
type BlobAndToken struct {
	*blob.Blob
	// Token is the continuation token to resume streaming
	// starting at this blob in the future.
	Token string
}

type BlobStreamer interface {
	// BlobStream is an optional interface that may be implemented by
	// Storage implementations.
	//
	// StreamBlobs sends blobs to dest in an unspecified order. It is
	// expected that a Storage implementation implementing
	// BlobStreamer will send blobs to dest in the most efficient
	// order possible.
	//
	// The provided continuation token resumes the stream at a
	// point. To start from the beginning, send the empty string.
	// The token is opaque and must never be interpreted; its
	// format may change between versions of the server.
	//
	// If the content is canceled, the error value is
	// context.ErrCanceled.
	//
	// StreamBlobs must unconditionally close dest before
	// returning, and it must return context.ErrCanceled if
	// ctx.Done() becomes readable.
	//
	// When StreamBlobs reaches the end, the return value is nil.
	StreamBlobs(ctx *context.Context, dest chan<- BlobAndToken, contToken string) error
}

// Cache is the minimal interface expected of a blob cache.
type Cache interface {
	blob.Fetcher
	BlobReceiver
	BlobStatter
}

type BlobReceiveConfiger interface {
	BlobReceiver
	Configer
}

type Config struct {
	Writable    bool
	Readable    bool
	Deletable   bool
	CanLongPoll bool

	// the "http://host:port" and optional path (but without trailing slash) to have "/camli/*" appended
	URLBase       string
	HandlerFinder FindHandlerByTyper
}

type BlobRemover interface {
	// RemoveBlobs removes 0 or more blobs.  Removal of
	// non-existent items isn't an error.  Returns failure if any
	// items existed but failed to be deleted.
	// ErrNotImplemented may be returned for storage types not implementing removal.
	RemoveBlobs(blobs []blob.Ref) error
}

// Storage is the interface that must be implemented by a blobserver
// storage type. (e.g. localdisk, s3, encrypt, shard, replica, remote)
type Storage interface {
	blob.Fetcher
	BlobReceiver
	BlobStatter
	BlobEnumerator
	BlobRemover
}

type FetcherEnumerator interface {
	blob.Fetcher
	BlobEnumerator
}

// StorageHandler is a storage implementation that also exports an HTTP
// status page.
type StorageHandler interface {
	Storage
	http.Handler
}

// Optional interface for storage implementations which can be asked
// to shut down cleanly. Regardless, all implementations should
// be able to survive crashes without data loss.
type ShutdownStorage interface {
	Storage
	io.Closer
}

// A GenerationNotSupportedError explains why a Storage
// value implemented the Generationer interface but failed due
// to a wrapped Storage value not implementing the interface.
type GenerationNotSupportedError string

func (s GenerationNotSupportedError) Error() string { return string(s) }

/*
The optional Generationer interface is an optimization and paranoia
facility for clients which can be implemented by Storage
implementations.

If the client sees the same random string in multiple upload sessions,
it assumes that the blobserver still has all the same blobs, and also
it's the same server.  This mechanism is not fundamental to
Camlistore's operation: the client could also check each blob before
uploading, or enumerate all blobs from the server too.  This is purely
an optimization so clients can mix this value into their "is this file
uploaded?" local cache keys.
*/
type Generationer interface {
	// Generation returns a Storage's initialization time and
	// and unique random string (or UUID).  Implementations
	// should call ResetStorageGeneration on demand if no
	// information is known.
	// The error will be of type GenerationNotSupportedError if an underlying
	// storage target doesn't support the Generationer interface.
	StorageGeneration() (initTime time.Time, random string, err error)

	// ResetGeneration deletes the information returned by Generation
	// and re-generates it.
	ResetStorageGeneration() error
}

type Configer interface {
	Config() *Config
}

type StorageConfiger interface {
	Storage
	Configer
}

// MaxEnumerateConfig is an optional interface implemented by Storage
// interfaces to advertise their max value for how many items can
// be enumerated at once.
type MaxEnumerateConfig interface {
	Storage

	// MaxEnumerate returns the max that this storage interface is
	// capable of enumerating at once.
	MaxEnumerate() int
}
<commit_msg>blobserver: add WholeRefFetcher interface<commit_after>/*
Copyright 2011 Google Inc.

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 blobserver

import (
	"errors"
	"io"
	"net/http"
	"os"
	"time"

	"camlistore.org/pkg/blob"
	"camlistore.org/pkg/constants"
	"camlistore.org/pkg/context"
)

// MaxBlobSize is the size of a single blob in Camlistore.
const MaxBlobSize = constants.MaxBlobSize

var ErrCorruptBlob = errors.New("corrupt blob; digest doesn't match")

// ErrNotImplemented should be returned in methods where the function is not implemented
var ErrNotImplemented = errors.New("not implemented")

// BlobReceiver is the interface for receiving
type BlobReceiver interface {
	// ReceiveBlob accepts a newly uploaded blob and writes it to
	// permanent storage.
	//
	// Implementations of BlobReceiver downstream of the HTTP
	// server can trust that the source isn't larger than
	// MaxBlobSize and that its digest matches the provided blob
	// ref. (If not, the read of the source will fail before EOF)
	//
	// To ensure those guarantees, callers of ReceiveBlob should
	// not call ReceiveBlob directly but instead use either
	// blobserver.Receive or blobserver.ReceiveString, which also
	// take care of notifying the BlobReceiver's "BlobHub"
	// notification bus for observers.
	ReceiveBlob(br blob.Ref, source io.Reader) (blob.SizedRef, error)
}

type BlobStatter interface {
	// Stat checks for the existence of blobs, writing their sizes
	// (if found back to the dest channel), and returning an error
	// or nil.  Stat() should NOT close the channel.
	// TODO(bradfitz): redefine this to close the channel? Or document
	// better what the synchronization rules are.
	StatBlobs(dest chan<- blob.SizedRef, blobs []blob.Ref) error
}

func StatBlob(bs BlobStatter, br blob.Ref) (sb blob.SizedRef, err error) {
	c := make(chan blob.SizedRef, 1)
	err = bs.StatBlobs(c, []blob.Ref{br})
	if err != nil {
		return
	}
	select {
	case sb = <-c:
	default:
		err = os.ErrNotExist
	}
	return
}

type StatReceiver interface {
	BlobReceiver
	BlobStatter
}

type BlobEnumerator interface {
	// EnumerateBobs sends at most limit SizedBlobRef into dest,
	// sorted, as long as they are lexigraphically greater than
	// after (if provided).
	// limit will be supplied and sanity checked by caller.
	// EnumerateBlobs must close the channel.  (even if limit
	// was hit and more blobs remain, or an error is returned, or
	// the ctx is canceled)
	EnumerateBlobs(ctx *context.Context,
		dest chan<- blob.SizedRef,
		after string,
		limit int) error

	// TODO: remove limit from this interface, since the caller
	// can cancel? see if that would simplify implementations and
	// callers.
}

// BlobAndToken is the value used by the BlobStreamer interface,
// containing both a Blob and a continuation token.
type BlobAndToken struct {
	*blob.Blob
	// Token is the continuation token to resume streaming
	// starting at this blob in the future.
	Token string
}

type BlobStreamer interface {
	// BlobStream is an optional interface that may be implemented by
	// Storage implementations.
	//
	// StreamBlobs sends blobs to dest in an unspecified order. It is
	// expected that a Storage implementation implementing
	// BlobStreamer will send blobs to dest in the most efficient
	// order possible.
	//
	// The provided continuation token resumes the stream at a
	// point. To start from the beginning, send the empty string.
	// The token is opaque and must never be interpreted; its
	// format may change between versions of the server.
	//
	// If the content is canceled, the error value is
	// context.ErrCanceled.
	//
	// StreamBlobs must unconditionally close dest before
	// returning, and it must return context.ErrCanceled if
	// ctx.Done() becomes readable.
	//
	// When StreamBlobs reaches the end, the return value is nil.
	StreamBlobs(ctx *context.Context, dest chan<- BlobAndToken, contToken string) error
}

// Cache is the minimal interface expected of a blob cache.
type Cache interface {
	blob.Fetcher
	BlobReceiver
	BlobStatter
}

type BlobReceiveConfiger interface {
	BlobReceiver
	Configer
}

type Config struct {
	Writable    bool
	Readable    bool
	Deletable   bool
	CanLongPoll bool

	// the "http://host:port" and optional path (but without trailing slash) to have "/camli/*" appended
	URLBase       string
	HandlerFinder FindHandlerByTyper
}

type BlobRemover interface {
	// RemoveBlobs removes 0 or more blobs.  Removal of
	// non-existent items isn't an error.  Returns failure if any
	// items existed but failed to be deleted.
	// ErrNotImplemented may be returned for storage types not implementing removal.
	RemoveBlobs(blobs []blob.Ref) error
}

// Storage is the interface that must be implemented by a blobserver
// storage type. (e.g. localdisk, s3, encrypt, shard, replica, remote)
type Storage interface {
	blob.Fetcher
	BlobReceiver
	BlobStatter
	BlobEnumerator
	BlobRemover
}

type FetcherEnumerator interface {
	blob.Fetcher
	BlobEnumerator
}

// StorageHandler is a storage implementation that also exports an HTTP
// status page.
type StorageHandler interface {
	Storage
	http.Handler
}

// Optional interface for storage implementations which can be asked
// to shut down cleanly. Regardless, all implementations should
// be able to survive crashes without data loss.
type ShutdownStorage interface {
	Storage
	io.Closer
}

// WholeRefFetcher is an optional fast-path interface exposed by the
// 'blobpacked' blob storage implementation, which packs pieces of
// files together and can efficiently serve them contigously.
type WholeRefFetcher interface {
	// OpenWholeRef returns a ReadCloser reading from offset bytes
	// into wholeRef (the blobref of an entire file).
	//
	// The returned wholeSize is the size of the file, without
	// subtracting any offset.
	//
	// The err will be os.ErrNotExist if the wholeref is not
	// known.OpenWholeRef
	OpenWholeRef(wholeRef blob.Ref, offset int64) (rc io.ReadCloser, wholeSize int64, err error)
}

// A GenerationNotSupportedError explains why a Storage
// value implemented the Generationer interface but failed due
// to a wrapped Storage value not implementing the interface.
type GenerationNotSupportedError string

func (s GenerationNotSupportedError) Error() string { return string(s) }

/*
The optional Generationer interface is an optimization and paranoia
facility for clients which can be implemented by Storage
implementations.

If the client sees the same random string in multiple upload sessions,
it assumes that the blobserver still has all the same blobs, and also
it's the same server.  This mechanism is not fundamental to
Camlistore's operation: the client could also check each blob before
uploading, or enumerate all blobs from the server too.  This is purely
an optimization so clients can mix this value into their "is this file
uploaded?" local cache keys.
*/
type Generationer interface {
	// Generation returns a Storage's initialization time and
	// and unique random string (or UUID).  Implementations
	// should call ResetStorageGeneration on demand if no
	// information is known.
	// The error will be of type GenerationNotSupportedError if an underlying
	// storage target doesn't support the Generationer interface.
	StorageGeneration() (initTime time.Time, random string, err error)

	// ResetGeneration deletes the information returned by Generation
	// and re-generates it.
	ResetStorageGeneration() error
}

type Configer interface {
	Config() *Config
}

type StorageConfiger interface {
	Storage
	Configer
}

// MaxEnumerateConfig is an optional interface implemented by Storage
// interfaces to advertise their max value for how many items can
// be enumerated at once.
type MaxEnumerateConfig interface {
	Storage

	// MaxEnumerate returns the max that this storage interface is
	// capable of enumerating at once.
	MaxEnumerate() int
}
<|endoftext|>


<commit_before>// Copyright (C) 2015 Scaleway. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE.md file.

package commands

import (
	"strings"
	"testing"

	. "github.com/smartystreets/goconvey/convey"
)

func ExampleRunImages() {
	ctx := testCommandContext()
	args := ImagesArgs{}
	RunImages(ctx, args)
}

func ExampleRunImages_complex() {
	ctx := testCommandContext()
	args := ImagesArgs{
		All:     false,
		NoTrunc: false,
		Quiet:   false,
	}
	RunImages(ctx, args)
}

func ExampleRunImages_quiet() {
	ctx := testCommandContext()
	args := ImagesArgs{
		All:     false,
		NoTrunc: false,
		Quiet:   true,
	}
	RunImages(ctx, args)
}

func ExampleRunImages_all() {
	ctx := testCommandContext()
	args := ImagesArgs{
		All:     true,
		NoTrunc: false,
		Quiet:   false,
	}
	RunImages(ctx, args)
}

func ExampleRunImages_notrunc() {
	ctx := testCommandContext()
	args := ImagesArgs{
		All:     false,
		NoTrunc: true,
		Quiet:   false,
	}
	RunImages(ctx, args)
}

func TestRunImages_realAPI(t *testing.T) {
	ctx := RealAPIContext()
	if ctx == nil {
		t.Skip()
	}
	Convey("Testing RunImages() on real API", t, func() {
		Convey("no options", func() {
			args := ImagesArgs{
				All:     false,
				NoTrunc: false,
				Quiet:   false,
			}

			scopedCtx, scopedStdout, scopedStderr := getScopedCtx(ctx)
			err := RunImages(*scopedCtx, args)
			So(err, ShouldBeNil)
			So(scopedStderr.String(), ShouldBeEmpty)

			lines := strings.Split(scopedStdout.String(), "\n")
			So(len(lines), ShouldBeGreaterThan, 0)

			firstLine := lines[0]
			colNames := strings.Fields(firstLine)
			So(colNames, ShouldResemble, []string{"REPOSITORY", "TAG", "IMAGE", "ID", "CREATED", "VIRTUAL", "SIZE"})

			// FIXME: test public images
		})
		Convey("--all", func() {
			args := ImagesArgs{
				All:     true,
				NoTrunc: false,
				Quiet:   false,
			}

			scopedCtx, scopedStdout, scopedStderr := getScopedCtx(ctx)
			err := RunImages(*scopedCtx, args)
			So(err, ShouldBeNil)
			So(scopedStderr.String(), ShouldBeEmpty)

			lines := strings.Split(scopedStdout.String(), "\n")
			So(len(lines), ShouldBeGreaterThan, 0)

			firstLine := lines[0]
			colNames := strings.Fields(firstLine)
			So(colNames, ShouldResemble, []string{"REPOSITORY", "TAG", "IMAGE", "ID", "CREATED", "VIRTUAL", "SIZE"})

			// FIXME: test public images
			// FIXME: test bootscripts
			// FIXME: test snapshots
		})
		Convey("--quiet", func() {
			args := ImagesArgs{
				All:     false,
				NoTrunc: false,
				Quiet:   true,
			}

			scopedCtx, scopedStdout, scopedStderr := getScopedCtx(ctx)
			err := RunImages(*scopedCtx, args)
			So(err, ShouldBeNil)
			So(scopedStderr.String(), ShouldBeEmpty)

			lines := strings.Split(scopedStdout.String(), "\n")
			// So(len(lines), ShouldBeGreaterThan, 0)

			if len(lines) > 0 {
				firstLine := lines[0]
				colNames := strings.Fields(firstLine)
				So(colNames, ShouldNotResemble, []string{"REPOSITORY", "TAG", "IMAGE", "ID", "CREATED", "VIRTUAL", "SIZE"})

				// FIXME: test public images
				// FIXME: test bootscripts
				// FIXME: test snapshots
			}
		})
	})
}
<commit_msg>Fix tests<commit_after>// Copyright (C) 2015 Scaleway. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE.md file.

package commands

import (
	"strings"
	"testing"

	. "github.com/smartystreets/goconvey/convey"
)

func ExampleRunImages() {
	ctx := testCommandContext()
	args := ImagesArgs{}
	RunImages(ctx, args)
}

func ExampleRunImages_complex() {
	ctx := testCommandContext()
	args := ImagesArgs{
		All:     false,
		NoTrunc: false,
		Quiet:   false,
	}
	RunImages(ctx, args)
}

func ExampleRunImages_quiet() {
	ctx := testCommandContext()
	args := ImagesArgs{
		All:     false,
		NoTrunc: false,
		Quiet:   true,
	}
	RunImages(ctx, args)
}

func ExampleRunImages_all() {
	ctx := testCommandContext()
	args := ImagesArgs{
		All:     true,
		NoTrunc: false,
		Quiet:   false,
	}
	RunImages(ctx, args)
}

func ExampleRunImages_notrunc() {
	ctx := testCommandContext()
	args := ImagesArgs{
		All:     false,
		NoTrunc: true,
		Quiet:   false,
	}
	RunImages(ctx, args)
}

func TestRunImages_realAPI(t *testing.T) {
	ctx := RealAPIContext()
	if ctx == nil {
		t.Skip()
	}
	Convey("Testing RunImages() on real API", t, func() {
		Convey("no options", func() {
			args := ImagesArgs{
				All:     false,
				NoTrunc: false,
				Quiet:   false,
			}

			scopedCtx, scopedStdout, scopedStderr := getScopedCtx(ctx)
			err := RunImages(*scopedCtx, args)
			So(err, ShouldBeNil)
			So(scopedStderr.String(), ShouldBeEmpty)

			lines := strings.Split(scopedStdout.String(), "\n")
			So(len(lines), ShouldBeGreaterThan, 0)

			firstLine := lines[0]
			colNames := strings.Fields(firstLine)
			So(colNames, ShouldResemble, []string{"REPOSITORY", "TAG", "IMAGE", "ID", "CREATED", "VIRTUAL", "SIZE", "REGION", "ARCH"})

			// FIXME: test public images
		})
		Convey("--all", func() {
			args := ImagesArgs{
				All:     true,
				NoTrunc: false,
				Quiet:   false,
			}

			scopedCtx, scopedStdout, scopedStderr := getScopedCtx(ctx)
			err := RunImages(*scopedCtx, args)
			So(err, ShouldBeNil)
			So(scopedStderr.String(), ShouldBeEmpty)

			lines := strings.Split(scopedStdout.String(), "\n")
			So(len(lines), ShouldBeGreaterThan, 0)

			firstLine := lines[0]
			colNames := strings.Fields(firstLine)
			So(colNames, ShouldResemble, []string{"REPOSITORY", "TAG", "IMAGE", "ID", "CREATED", "VIRTUAL", "SIZE", "REGION", "ARCH"})

			// FIXME: test public images
			// FIXME: test bootscripts
			// FIXME: test snapshots
		})
		Convey("--quiet", func() {
			args := ImagesArgs{
				All:     false,
				NoTrunc: false,
				Quiet:   true,
			}

			scopedCtx, scopedStdout, scopedStderr := getScopedCtx(ctx)
			err := RunImages(*scopedCtx, args)
			So(err, ShouldBeNil)
			So(scopedStderr.String(), ShouldBeEmpty)

			lines := strings.Split(scopedStdout.String(), "\n")
			// So(len(lines), ShouldBeGreaterThan, 0)

			if len(lines) > 0 {
				firstLine := lines[0]
				colNames := strings.Fields(firstLine)
				So(colNames, ShouldNotResemble, []string{"REPOSITORY", "TAG", "IMAGE", "ID", "CREATED", "VIRTUAL", "SIZE"})

				// FIXME: test public images
				// FIXME: test bootscripts
				// FIXME: test snapshots
			}
		})
	})
}
<|endoftext|>


<commit_before>// Package ocb2 implements the version 2 of the OCB authenticated-encryption algorithm.
// OCB2 is specified in http://www.cs.ucdavis.edu/~rogaway/papers/draft-krovetz-ocb-00.txt.
//
// It should be noted that OCB's author, Phil Rogaway <rogaway@cs.ucdavis.edu>, holds
// several US patents on the algorithm.  This should be considered before using this code
// in your own projects.  See OCB's FAQ for more info:
// http://www.cs.ucdavis.edu/~rogaway/ocb/ocb-faq.htm#patent:phil
//
// The Mumble Project has a license to use OCB mode in its BSD licensed code on a royalty
// free basis.
package ocb2

import (
	"crypto/aes"
	"crypto/cipher"
)

const (
	// TagSize specifies the length in bytes of a full OCB2 tag.
	// As per the specification, applications may truncate their
	// tags to a given length, but advocates that typical applications
	// should use a tag length of at least 8 bytes (64 bits).
	TagSize = aes.BlockSize
	// NonceSize specifies the length in bytes of an OCB2 nonce.
	NonceSize = aes.BlockSize
)

// zeros fills block with zero bytes.
func zeros(block []byte) {
	for i := range block {
		block[i] = 0
	}
}

// xor outputs the bitwise exclusive-or of a and b to dst.
func xor(dst []byte, a []byte, b []byte) {
	for i := 0; i < aes.BlockSize; i++ {
		dst[i] = a[i] ^ b[i]
	}
}

// times2 performs the times2 operation, defined as:
//
// times2(S)
//     S << 1 if S[1] = 0, and (S << 1) xor const(bitlength(S)) if S[1] = 1.
//
// where const(n) is defined as
//
// const(n)
//     The lexicographically first n-bit string C among all
//     strings that have a minimal possible number of "1"
//     bits and which name a polynomial x^n + C[1] *
//     x^{n-1} + ... + C[n-1] * x^1 + C[n] * x^0 that is
//     irreducible over the field with two elements.  In
//     particular, const(128) = num2str(135, 128).  For
//     other values of n, refer to a standard table of
//     irreducible polynomials [G. Seroussi,
//     "Table of low-weight binary irreducible polynomials",
//     HP Labs Technical Report HPL-98-135, 1998.].
func times2(block []byte) {
	carry := (block[0] >> 7) & 0x1
	for i := 0; i < aes.BlockSize-1; i++ {
		block[i] = (block[i] << 1) | ((block[i+1] >> 7) & 0x1)
	}
	block[aes.BlockSize-1] = (block[aes.BlockSize-1] << 1) ^ (carry * 135)
}

// times3 performs the times3 operation, defined as:
//
// times3(S)
//     times2(S) xor S
func times3(block []byte) {
	carry := (block[0] >> 7) & 0x1
	for i := 0; i < aes.BlockSize-1; i++ {
		block[i] ^= (block[i] << 1) | ((block[i+1] >> 7) & 0x1)
	}
	block[aes.BlockSize-1] ^= ((block[aes.BlockSize-1] << 1) ^ (carry * 135))
}

// Encrypt encrypts the plaintext src and outputs the corresponding ciphertext into dst.
// Besides outputting a ciphertext into dst, Encrypt also outputs an authentication tag
// of ocb2.TagSize bytes into tag, which should be used to verify the authenticity of the
// message on the receiving side.
//
// To ensure both authenticity and secrecy of messages, each invocation to this function must
// be given an unique nonce of ocb2.NonceSize bytes.  The nonce need not be secret (it can be
// a counter), but it needs to be unique.
func Encrypt(cipher cipher.Block, dst []byte, src []byte, nonce []byte, tag []byte) {
	var delta [aes.BlockSize]byte
	var checksum [aes.BlockSize]byte
	var tmp [aes.BlockSize]byte
	var pad [aes.BlockSize]byte
	off := 0

	cipher.Encrypt(delta[0:], nonce[0:])
	zeros(checksum[0:])

	remain := len(src)
	for remain > aes.BlockSize {
		times2(delta[0:])
		xor(tmp[0:], delta[0:], src[off:off+aes.BlockSize])
		cipher.Encrypt(tmp[0:], tmp[0:])
		xor(dst[off:off+aes.BlockSize], delta[0:], tmp[0:])
		xor(checksum[0:], checksum[0:], src[off:off+aes.BlockSize])
		remain -= aes.BlockSize
		off += aes.BlockSize
	}

	times2(delta[0:])
	zeros(tmp[0:])
	num := remain * 8
	tmp[aes.BlockSize-2] = uint8((uint32(num) >> 8) & 0xff)
	tmp[aes.BlockSize-1] = uint8(num & 0xff)
	xor(tmp[0:], tmp[0:], delta[0:])
	cipher.Encrypt(pad[0:], tmp[0:])
	copied := copy(tmp[0:], src[off:])
	if copied != remain {
		panic("ocb2: copy failed")
	}
	if copy(tmp[copied:], pad[copied:]) != (aes.BlockSize - remain) {
		panic("ocb2: copy failed")
	}
	xor(checksum[0:], checksum[0:], tmp[0:])
	xor(tmp[0:], pad[0:], tmp[0:])
	if copy(dst[off:], tmp[0:]) != remain {
		panic("ocb2: copy failed")
	}

	times3(delta[0:])
	xor(tmp[0:], delta[0:], checksum[0:])
	cipher.Encrypt(tag[0:], tmp[0:])
}

// Decrypt takes a ciphertext and a nonce as its input and outputs a decrypted plaintext
// and corresponding authentication tag.
//
// Before using the decrpyted plaintext, the application
// should verify that the computed authentication tag matches the tag that was produced when
// encrypting the message (taking into consideration that OCB tags are allowed to be truncated
// to a length less than ocb.TagSize).
func Decrypt(cipher cipher.Block, plain []byte, encrypted []byte, nonce []byte, tag []byte) {
	var checksum [aes.BlockSize]byte
	var delta [aes.BlockSize]byte
	var tmp [aes.BlockSize]byte
	var pad [aes.BlockSize]byte
	off := 0

	cipher.Encrypt(delta[0:], nonce[0:])
	zeros(checksum[0:])

	remain := len(encrypted)
	for remain > aes.BlockSize {
		times2(delta[0:])
		xor(tmp[0:], delta[0:], encrypted[off:off+aes.BlockSize])
		cipher.Decrypt(tmp[0:], tmp[0:])
		xor(plain[off:off+aes.BlockSize], delta[0:], tmp[0:])
		xor(checksum[0:], checksum[0:], plain[off:off+aes.BlockSize])
		off += aes.BlockSize
		remain -= aes.BlockSize
	}

	times2(delta[0:])
	zeros(tmp[0:])
	num := remain * 8
	tmp[aes.BlockSize-2] = uint8((uint32(num) >> 8) & 0xff)
	tmp[aes.BlockSize-1] = uint8(num & 0xff)
	xor(tmp[0:], tmp[0:], delta[0:])
	cipher.Encrypt(pad[0:], tmp[0:])
	zeros(tmp[0:])
	copied := copy(tmp[0:remain], encrypted[off:off+remain])
	if copied != remain {
		panic("ocb2: copy failed")
	}
	xor(tmp[0:], tmp[0:], pad[0:])
	xor(checksum[0:], checksum[0:], tmp[0:])
	copied = copy(plain[off:off+remain], tmp[0:remain])
	if copied != remain {
		panic("ocb2: copy failed")
	}

	times3(delta[0:])
	xor(tmp[0:], delta[0:], checksum[0:])
	cipher.Encrypt(tag[0:], tmp[0:])
}<commit_msg>pkg/cryptstate/ocb2: update times2 documentation.<commit_after>// Package ocb2 implements the version 2 of the OCB authenticated-encryption algorithm.
// OCB2 is specified in http://www.cs.ucdavis.edu/~rogaway/papers/draft-krovetz-ocb-00.txt.
//
// It should be noted that OCB's author, Phil Rogaway <rogaway@cs.ucdavis.edu>, holds
// several US patents on the algorithm.  This should be considered before using this code
// in your own projects.  See OCB's FAQ for more info:
// http://www.cs.ucdavis.edu/~rogaway/ocb/ocb-faq.htm#patent:phil
//
// The Mumble Project has a license to use OCB mode in its BSD licensed code on a royalty
// free basis.
package ocb2

import (
	"crypto/aes"
	"crypto/cipher"
)

const (
	// TagSize specifies the length in bytes of a full OCB2 tag.
	// As per the specification, applications may truncate their
	// tags to a given length, but advocates that typical applications
	// should use a tag length of at least 8 bytes (64 bits).
	TagSize = aes.BlockSize
	// NonceSize specifies the length in bytes of an OCB2 nonce.
	NonceSize = aes.BlockSize
)

// zeros fills block with zero bytes.
func zeros(block []byte) {
	for i := range block {
		block[i] = 0
	}
}

// xor outputs the bitwise exclusive-or of a and b to dst.
func xor(dst []byte, a []byte, b []byte) {
	for i := 0; i < aes.BlockSize; i++ {
		dst[i] = a[i] ^ b[i]
	}
}

// times2 performs the times2 operation, defined as:
//
// times2(S)
//     S << 1 if S[1] = 0, and (S << 1) xor const(bitlength(S)) if S[1] = 1.
//
// where const(n) is defined as
//
// const(n)
//     The lexicographically first n-bit string C among all
//     strings that have a minimal possible number of "1"
//     bits and which name a polynomial x^n + C[1] *
//     x^{n-1} + ... + C[n-1] * x^1 + C[n] * x^0 that is
//     irreducible over the field with two elements.  In
//     particular, const(128) = num2str(135, 128).  For
//     other values of n, refer to a standard table of
//     irreducible polynomials [G. Seroussi,
//     "Table of low-weight binary irreducible polynomials",
//     HP Labs Technical Report HPL-98-135, 1998.].
//
// and num2str(x, n) is defined as
//
// num2str(x, n)
//     The n-bit binary representation of the integer x.
//     More formally, the n-bit string S where x = S[1] *
//     2^{n-1} + S[2] * 2^{n-2} + ... + S[n] * 2^{0}.  Only
//     used when 0 <= x < 2^n.
//
// For our 128-bit block size implementation, this means that
// the xor with const(bitlength(S)) if S[1] = 1 is implemented
// by simply xor'ing the last byte with the number 135 when
// S[1] = 1.
func times2(block []byte) {
	carry := (block[0] >> 7) & 0x1
	for i := 0; i < aes.BlockSize-1; i++ {
		block[i] = (block[i] << 1) | ((block[i+1] >> 7) & 0x1)
	}
	block[aes.BlockSize-1] = (block[aes.BlockSize-1] << 1) ^ (carry * 135)
}

// times3 performs the times3 operation, defined as:
//
// times3(S)
//     times2(S) xor S
func times3(block []byte) {
	carry := (block[0] >> 7) & 0x1
	for i := 0; i < aes.BlockSize-1; i++ {
		block[i] ^= (block[i] << 1) | ((block[i+1] >> 7) & 0x1)
	}
	block[aes.BlockSize-1] ^= ((block[aes.BlockSize-1] << 1) ^ (carry * 135))
}

// Encrypt encrypts the plaintext src and outputs the corresponding ciphertext into dst.
// Besides outputting a ciphertext into dst, Encrypt also outputs an authentication tag
// of ocb2.TagSize bytes into tag, which should be used to verify the authenticity of the
// message on the receiving side.
//
// To ensure both authenticity and secrecy of messages, each invocation to this function must
// be given an unique nonce of ocb2.NonceSize bytes.  The nonce need not be secret (it can be
// a counter), but it needs to be unique.
func Encrypt(cipher cipher.Block, dst []byte, src []byte, nonce []byte, tag []byte) {
	var delta [aes.BlockSize]byte
	var checksum [aes.BlockSize]byte
	var tmp [aes.BlockSize]byte
	var pad [aes.BlockSize]byte
	off := 0

	cipher.Encrypt(delta[0:], nonce[0:])
	zeros(checksum[0:])

	remain := len(src)
	for remain > aes.BlockSize {
		times2(delta[0:])
		xor(tmp[0:], delta[0:], src[off:off+aes.BlockSize])
		cipher.Encrypt(tmp[0:], tmp[0:])
		xor(dst[off:off+aes.BlockSize], delta[0:], tmp[0:])
		xor(checksum[0:], checksum[0:], src[off:off+aes.BlockSize])
		remain -= aes.BlockSize
		off += aes.BlockSize
	}

	times2(delta[0:])
	zeros(tmp[0:])
	num := remain * 8
	tmp[aes.BlockSize-2] = uint8((uint32(num) >> 8) & 0xff)
	tmp[aes.BlockSize-1] = uint8(num & 0xff)
	xor(tmp[0:], tmp[0:], delta[0:])
	cipher.Encrypt(pad[0:], tmp[0:])
	copied := copy(tmp[0:], src[off:])
	if copied != remain {
		panic("ocb2: copy failed")
	}
	if copy(tmp[copied:], pad[copied:]) != (aes.BlockSize - remain) {
		panic("ocb2: copy failed")
	}
	xor(checksum[0:], checksum[0:], tmp[0:])
	xor(tmp[0:], pad[0:], tmp[0:])
	if copy(dst[off:], tmp[0:]) != remain {
		panic("ocb2: copy failed")
	}

	times3(delta[0:])
	xor(tmp[0:], delta[0:], checksum[0:])
	cipher.Encrypt(tag[0:], tmp[0:])
}

// Decrypt takes a ciphertext and a nonce as its input and outputs a decrypted plaintext
// and corresponding authentication tag.
//
// Before using the decrpyted plaintext, the application
// should verify that the computed authentication tag matches the tag that was produced when
// encrypting the message (taking into consideration that OCB tags are allowed to be truncated
// to a length less than ocb.TagSize).
func Decrypt(cipher cipher.Block, plain []byte, encrypted []byte, nonce []byte, tag []byte) {
	var checksum [aes.BlockSize]byte
	var delta [aes.BlockSize]byte
	var tmp [aes.BlockSize]byte
	var pad [aes.BlockSize]byte
	off := 0

	cipher.Encrypt(delta[0:], nonce[0:])
	zeros(checksum[0:])

	remain := len(encrypted)
	for remain > aes.BlockSize {
		times2(delta[0:])
		xor(tmp[0:], delta[0:], encrypted[off:off+aes.BlockSize])
		cipher.Decrypt(tmp[0:], tmp[0:])
		xor(plain[off:off+aes.BlockSize], delta[0:], tmp[0:])
		xor(checksum[0:], checksum[0:], plain[off:off+aes.BlockSize])
		off += aes.BlockSize
		remain -= aes.BlockSize
	}

	times2(delta[0:])
	zeros(tmp[0:])
	num := remain * 8
	tmp[aes.BlockSize-2] = uint8((uint32(num) >> 8) & 0xff)
	tmp[aes.BlockSize-1] = uint8(num & 0xff)
	xor(tmp[0:], tmp[0:], delta[0:])
	cipher.Encrypt(pad[0:], tmp[0:])
	zeros(tmp[0:])
	copied := copy(tmp[0:remain], encrypted[off:off+remain])
	if copied != remain {
		panic("ocb2: copy failed")
	}
	xor(tmp[0:], tmp[0:], pad[0:])
	xor(checksum[0:], checksum[0:], tmp[0:])
	copied = copy(plain[off:off+remain], tmp[0:remain])
	if copied != remain {
		panic("ocb2: copy failed")
	}

	times3(delta[0:])
	xor(tmp[0:], delta[0:], checksum[0:])
	cipher.Encrypt(tag[0:], tmp[0:])
}<|endoftext|>


<commit_before>/*
Copyright 2020 Google LLC

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 signer

import (
	"fmt"

	"github.com/golang/glog"
	"github.com/grafeas/kritis/pkg/attestlib"
	"github.com/grafeas/kritis/pkg/kritis/apis/kritis/v1beta1"
	"github.com/grafeas/kritis/pkg/kritis/attestation"
	"github.com/grafeas/kritis/pkg/kritis/crd/authority"
	"github.com/grafeas/kritis/pkg/kritis/metadata"
	"github.com/grafeas/kritis/pkg/kritis/util"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// A signer is used for creating attestations for an image.
type Signer struct {
	config *config
	client metadata.ReadWriteClient
}

// A signer config that includes necessary data and handler for signing.
type config struct {
	cSigner attestlib.Signer
	// an AttestaionAuthority that is used in metadata client APIs.
	// We should consider refactor it out because:
	// 1. the only useful field here is noteName
	// 2. other fields, e.g., public key, are unset
	// TODO: refactor out the authority code
	authority v1beta1.AttestationAuthority
	project   string
	overwrite bool
}

// Creating a new signer object.
func New(client metadata.ReadWriteClient, cSigner attestlib.Signer, noteName string, project string, overwrite bool) Signer {
	return Signer{
		client: client,
		config: &config{
			cSigner,
			v1beta1.AttestationAuthority{
				ObjectMeta: metav1.ObjectMeta{Name: "signing-aa"},
				Spec: v1beta1.AttestationAuthoritySpec{
					NoteReference: noteName,
					PublicKeys:    []v1beta1.PublicKey{},
				},
			},
			project,
			overwrite,
		},
	}
}

// ImageVulnerabilities is an input for running vulnerability policy validation.
type ImageVulnerabilities struct {
	ImageRef        string
	Vulnerabilities []metadata.Vulnerability
}

// For testing
var (
	authFetcher = authority.Authority
)

// SignImage signs an image without doing any policy check.
// Returns an error if creating an attestation fails.
func (s Signer) SignImage(image string) error {
	existed, err := s.isAttestationAlreadyExist(image)
	if err != nil {
		return fmt.Errorf("checking existing attestation status failed: %v", err)
	}
	if !existed {
		glog.Infof("No existing attestation was found for image %q.", image)
	}
	if existed && !s.config.overwrite {
		glog.Warningf("Attestation for image %q already existed and signer is configured not to overwrite.", image)
		return nil
	}
	if existed && s.config.overwrite {
			glog.Infof("Deleting existing attestation for image %q because signer.config.overwrite=True.", image)
			err := s.client.DeleteAttestationOccurrence(image, &s.config.authority)
			if err != nil {
				return fmt.Errorf("deleting existing attestation failed: %v", err)
			}
	}

	glog.Infof("Creating attestation for image %q.", image)
	// Create attestation
	att, err := s.createAttestation(image)
	if err != nil {
		return fmt.Errorf("creatiing attestation failed: %v", err)
	}
	glog.Infof("Attestation for image %q is successfully created locally.", image)

	glog.Infof("Uploading attestation for image %q.", image)
	if err := s.uploadAttestation(image, att); err != nil {
		return fmt.Errorf("uploading attestation failed: %v", err)
	}
	glog.Infof("Attestation for image %q is successfully uploaded.", image)

	return nil
}

// Creating an atestation.
func (s Signer) createAttestation(image string) (*attestlib.Attestation, error) {
	payload, err := attestation.AtomicContainerPayload(image)
	if err != nil {
		return nil, err
	}

	att, err := s.config.cSigner.CreateAttestation(payload)
	if err != nil {
		return nil, err
	}
	return att, nil
}

// Uploading an attestation if not already exist under the same note.
// The method will create a note if it does not already exist.
// Returns error if upload failed, e.g., if an attestation already exists.
func (s Signer) uploadAttestation(image string, att *attestlib.Attestation) error {
	note, err := util.GetOrCreateAttestationNote(s.client, &s.config.authority)
	if err != nil {
		return err
	}

	// Upload attestation
	_, err = s.client.UploadAttestationOccurrence(note.GetName(), image, att, s.config.project, metadata.GenericSignatureType)
	return err
}

func (s Signer) isAttestationAlreadyExist(image string) (bool, error) {
	atts, err := s.client.Attestations(image, &s.config.authority)
	if err == nil && len(atts) > 0 {
		return true, nil
	}

	return false, err
}
<commit_msg>Format.<commit_after>/*
Copyright 2020 Google LLC

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 signer

import (
	"fmt"

	"github.com/golang/glog"
	"github.com/grafeas/kritis/pkg/attestlib"
	"github.com/grafeas/kritis/pkg/kritis/apis/kritis/v1beta1"
	"github.com/grafeas/kritis/pkg/kritis/attestation"
	"github.com/grafeas/kritis/pkg/kritis/crd/authority"
	"github.com/grafeas/kritis/pkg/kritis/metadata"
	"github.com/grafeas/kritis/pkg/kritis/util"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// A signer is used for creating attestations for an image.
type Signer struct {
	config *config
	client metadata.ReadWriteClient
}

// A signer config that includes necessary data and handler for signing.
type config struct {
	cSigner attestlib.Signer
	// an AttestaionAuthority that is used in metadata client APIs.
	// We should consider refactor it out because:
	// 1. the only useful field here is noteName
	// 2. other fields, e.g., public key, are unset
	// TODO: refactor out the authority code
	authority v1beta1.AttestationAuthority
	project   string
	overwrite bool
}

// Creating a new signer object.
func New(client metadata.ReadWriteClient, cSigner attestlib.Signer, noteName string, project string, overwrite bool) Signer {
	return Signer{
		client: client,
		config: &config{
			cSigner,
			v1beta1.AttestationAuthority{
				ObjectMeta: metav1.ObjectMeta{Name: "signing-aa"},
				Spec: v1beta1.AttestationAuthoritySpec{
					NoteReference: noteName,
					PublicKeys:    []v1beta1.PublicKey{},
				},
			},
			project,
			overwrite,
		},
	}
}

// ImageVulnerabilities is an input for running vulnerability policy validation.
type ImageVulnerabilities struct {
	ImageRef        string
	Vulnerabilities []metadata.Vulnerability
}

// For testing
var (
	authFetcher = authority.Authority
)

// SignImage signs an image without doing any policy check.
// Returns an error if creating an attestation fails.
func (s Signer) SignImage(image string) error {
	existed, err := s.isAttestationAlreadyExist(image)
	if err != nil {
		return fmt.Errorf("checking existing attestation status failed: %v", err)
	}
	if !existed {
		glog.Infof("No existing attestation was found for image %q.", image)
	}
	if existed && !s.config.overwrite {
		glog.Warningf("Attestation for image %q already existed and signer is configured not to overwrite.", image)
		return nil
	}
	if existed && s.config.overwrite {
		glog.Infof("Deleting existing attestation for image %q because signer.config.overwrite=True.", image)
		err := s.client.DeleteAttestationOccurrence(image, &s.config.authority)
		if err != nil {
			return fmt.Errorf("deleting existing attestation failed: %v", err)
		}
	}

	glog.Infof("Creating attestation for image %q.", image)
	// Create attestation
	att, err := s.createAttestation(image)
	if err != nil {
		return fmt.Errorf("creatiing attestation failed: %v", err)
	}
	glog.Infof("Attestation for image %q is successfully created locally.", image)

	glog.Infof("Uploading attestation for image %q.", image)
	if err := s.uploadAttestation(image, att); err != nil {
		return fmt.Errorf("uploading attestation failed: %v", err)
	}
	glog.Infof("Attestation for image %q is successfully uploaded.", image)

	return nil
}

// Creating an atestation.
func (s Signer) createAttestation(image string) (*attestlib.Attestation, error) {
	payload, err := attestation.AtomicContainerPayload(image)
	if err != nil {
		return nil, err
	}

	att, err := s.config.cSigner.CreateAttestation(payload)
	if err != nil {
		return nil, err
	}
	return att, nil
}

// Uploading an attestation if not already exist under the same note.
// The method will create a note if it does not already exist.
// Returns error if upload failed, e.g., if an attestation already exists.
func (s Signer) uploadAttestation(image string, att *attestlib.Attestation) error {
	note, err := util.GetOrCreateAttestationNote(s.client, &s.config.authority)
	if err != nil {
		return err
	}

	// Upload attestation
	_, err = s.client.UploadAttestationOccurrence(note.GetName(), image, att, s.config.project, metadata.GenericSignatureType)
	return err
}

func (s Signer) isAttestationAlreadyExist(image string) (bool, error) {
	atts, err := s.client.Attestations(image, &s.config.authority)
	if err == nil && len(atts) > 0 {
		return true, nil
	}

	return false, err
}
<|endoftext|>