<commit_before>package google

import (
	"fmt"
	"os"
	"strings"
	"testing"

	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
	"google.golang.org/api/cloudresourcemanager/v1"
)

var (
	org = multiEnvSearch([]string{
		"GOOGLE_ORG",
	})

	pname          = "Terraform Acceptance Tests"
	originalPolicy *cloudresourcemanager.Policy
)

func multiEnvSearch(ks []string) string {
	for _, k := range ks {
		if v := os.Getenv(k); v != "" {
			return v
		}
	}
	return ""
}

// Test that a Project resource can be created and an IAM policy
// associated
func TestAccGoogleProject_create(t *testing.T) {
	pid := "terraform-" + acctest.RandString(10)
	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			// This step imports an existing project
			resource.TestStep{
				Config: testAccGoogleProject_create(pid, pname, org),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckGoogleProjectExists("google_project.acceptance", pid),
				),
			},
		},
	})
}

// Test that a Project resource can be created with an associated
// billing account
func TestAccGoogleProject_createBilling(t *testing.T) {
	skipIfEnvNotSet(t,
		[]string{
			"GOOGLE_ORG",
			"GOOGLE_BILLING_ACCOUNT",
		}...,
	)

	billingId := os.Getenv("GOOGLE_BILLING_ACCOUNT")
	pid := "terraform-" + acctest.RandString(10)
	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			// This step creates a new project with a billing account
			resource.TestStep{
				Config: testAccGoogleProject_createBilling(pid, pname, org, billingId),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckGoogleProjectHasBillingAccount("google_project.acceptance", pid, billingId),
				),
			},
		},
	})
}

// Test that a Project resource can be created and updated
// with billing account information
func TestAccGoogleProject_updateBilling(t *testing.T) {
	skipIfEnvNotSet(t,
		[]string{
			"GOOGLE_ORG",
			"GOOGLE_BILLING_ACCOUNT",
			"GOOGLE_BILLING_ACCOUNT_2",
		}...,
	)

	billingId := os.Getenv("GOOGLE_BILLING_ACCOUNT")
	billingId2 := os.Getenv("GOOGLE_BILLING_ACCOUNT_2")
	pid := "terraform-" + acctest.RandString(10)
	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			// This step creates a new project without a billing account
			resource.TestStep{
				Config: testAccGoogleProject_create(pid, pname, org),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckGoogleProjectExists("google_project.acceptance", pid),
				),
			},
			// Update to include a billing account
			resource.TestStep{
				Config: testAccGoogleProject_createBilling(pid, pname, org, billingId),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckGoogleProjectHasBillingAccount("google_project.acceptance", pid, billingId),
				),
			},
			// Update to a different  billing account
			resource.TestStep{
				Config: testAccGoogleProject_createBilling(pid, pname, org, billingId2),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckGoogleProjectHasBillingAccount("google_project.acceptance", pid, billingId2),
				),
			},
		},
	})
}

// Test that a Project resource merges the IAM policies that already
// exist, and won't lock people out.
func TestAccGoogleProject_merge(t *testing.T) {
	pid := "terraform-" + acctest.RandString(10)
	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			// when policy_data is set, merge
			{
				Config: testAccGoogleProject_toMerge(pid, pname, org),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckGoogleProjectExists("google_project.acceptance", pid),
					testAccCheckGoogleProjectHasMoreBindingsThan(pid, 1),
				),
			},
			// when policy_data is unset, restore to what it was
			{
				Config: testAccGoogleProject_mergeEmpty(pid, pname, org),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckGoogleProjectExists("google_project.acceptance", pid),
					testAccCheckGoogleProjectHasMoreBindingsThan(pid, 0),
				),
			},
		},
	})
}

func testAccCheckGoogleProjectExists(r, pid string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[r]
		if !ok {
			return fmt.Errorf("Not found: %s", r)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No ID is set")
		}

		if rs.Primary.ID != pid {
			return fmt.Errorf("Expected project %q to match ID %q in state", pid, rs.Primary.ID)
		}

		return nil
	}
}

func testAccCheckGoogleProjectHasBillingAccount(r, pid, billingId string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[r]
		if !ok {
			return fmt.Errorf("Not found: %s", r)
		}

		// State should match expected
		if rs.Primary.Attributes["billing_account"] != billingId {
			return fmt.Errorf("Billing ID in state (%s) does not match expected value (%s)", rs.Primary.Attributes["billing_account"], billingId)
		}

		// Actual value in API should match state and expected
		// Read the billing account
		config := testAccProvider.Meta().(*Config)
		ba, err := config.clientBilling.Projects.GetBillingInfo(prefixedProject(pid)).Do()
		if err != nil {
			return fmt.Errorf("Error reading billing account for project %q: %v", prefixedProject(pid), err)
		}
		if billingId != strings.TrimPrefix(ba.BillingAccountName, "billingAccounts/") {
			return fmt.Errorf("Billing ID returned by API (%s) did not match expected value (%s)", ba.BillingAccountName, billingId)
		}
		return nil
	}
}

func testAccCheckGoogleProjectHasMoreBindingsThan(pid string, count int) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		policy, err := getProjectIamPolicy(pid, testAccProvider.Meta().(*Config))
		if err != nil {
			return err
		}
		if len(policy.Bindings) <= count {
			return fmt.Errorf("Expected more than %d bindings, got %d: %#v", count, len(policy.Bindings), policy.Bindings)
		}
		return nil
	}
}

func testAccGoogleProjectImportExisting(pid string) string {
	return fmt.Sprintf(`
resource "google_project" "acceptance" {
    project_id = "%s"

}
`, pid)
}

func testAccGoogleProject_toMerge(pid, name, org string) string {
	return fmt.Sprintf(`
resource "google_project" "acceptance" {
    project_id = "%s"
    name = "%s"
    org_id = "%s"
}

resource "google_project_iam_policy" "acceptance" {
    project = "${google_project.acceptance.project_id}"
    policy_data = "${data.google_iam_policy.acceptance.policy_data}"
}

data "google_iam_policy" "acceptance" {
    binding {
        role = "roles/storage.objectViewer"
	members = [
	  "user:evanbrown@google.com",
	]
    }
}`, pid, name, org)
}

func testAccGoogleProject_mergeEmpty(pid, name, org string) string {
	return fmt.Sprintf(`
resource "google_project" "acceptance" {
    project_id = "%s"
    name = "%s"
    org_id = "%s"
}`, pid, name, org)
}

func skipIfEnvNotSet(t *testing.T, envs ...string) {
	for _, k := range envs {
		if os.Getenv(k) == "" {
			t.Skipf("Environment variable %s is not set", k)
		}
	}
}
<commit_msg>Prune dead function.<commit_after>package google

import (
	"fmt"
	"os"
	"strings"
	"testing"

	"github.com/hashicorp/terraform/helper/acctest"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
	"google.golang.org/api/cloudresourcemanager/v1"
)

var (
	org = multiEnvSearch([]string{
		"GOOGLE_ORG",
	})

	pname          = "Terraform Acceptance Tests"
	originalPolicy *cloudresourcemanager.Policy
)

func multiEnvSearch(ks []string) string {
	for _, k := range ks {
		if v := os.Getenv(k); v != "" {
			return v
		}
	}
	return ""
}

// Test that a Project resource can be created and an IAM policy
// associated
func TestAccGoogleProject_create(t *testing.T) {
	pid := "terraform-" + acctest.RandString(10)
	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			// This step imports an existing project
			resource.TestStep{
				Config: testAccGoogleProject_create(pid, pname, org),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckGoogleProjectExists("google_project.acceptance", pid),
				),
			},
		},
	})
}

// Test that a Project resource can be created with an associated
// billing account
func TestAccGoogleProject_createBilling(t *testing.T) {
	skipIfEnvNotSet(t,
		[]string{
			"GOOGLE_ORG",
			"GOOGLE_BILLING_ACCOUNT",
		}...,
	)

	billingId := os.Getenv("GOOGLE_BILLING_ACCOUNT")
	pid := "terraform-" + acctest.RandString(10)
	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			// This step creates a new project with a billing account
			resource.TestStep{
				Config: testAccGoogleProject_createBilling(pid, pname, org, billingId),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckGoogleProjectHasBillingAccount("google_project.acceptance", pid, billingId),
				),
			},
		},
	})
}

// Test that a Project resource can be created and updated
// with billing account information
func TestAccGoogleProject_updateBilling(t *testing.T) {
	skipIfEnvNotSet(t,
		[]string{
			"GOOGLE_ORG",
			"GOOGLE_BILLING_ACCOUNT",
			"GOOGLE_BILLING_ACCOUNT_2",
		}...,
	)

	billingId := os.Getenv("GOOGLE_BILLING_ACCOUNT")
	billingId2 := os.Getenv("GOOGLE_BILLING_ACCOUNT_2")
	pid := "terraform-" + acctest.RandString(10)
	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			// This step creates a new project without a billing account
			resource.TestStep{
				Config: testAccGoogleProject_create(pid, pname, org),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckGoogleProjectExists("google_project.acceptance", pid),
				),
			},
			// Update to include a billing account
			resource.TestStep{
				Config: testAccGoogleProject_createBilling(pid, pname, org, billingId),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckGoogleProjectHasBillingAccount("google_project.acceptance", pid, billingId),
				),
			},
			// Update to a different  billing account
			resource.TestStep{
				Config: testAccGoogleProject_createBilling(pid, pname, org, billingId2),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckGoogleProjectHasBillingAccount("google_project.acceptance", pid, billingId2),
				),
			},
		},
	})
}

// Test that a Project resource merges the IAM policies that already
// exist, and won't lock people out.
func TestAccGoogleProject_merge(t *testing.T) {
	pid := "terraform-" + acctest.RandString(10)
	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			// when policy_data is set, merge
			{
				Config: testAccGoogleProject_toMerge(pid, pname, org),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckGoogleProjectExists("google_project.acceptance", pid),
					testAccCheckGoogleProjectHasMoreBindingsThan(pid, 1),
				),
			},
			// when policy_data is unset, restore to what it was
			{
				Config: testAccGoogleProject_mergeEmpty(pid, pname, org),
				Check: resource.ComposeTestCheckFunc(
					testAccCheckGoogleProjectExists("google_project.acceptance", pid),
					testAccCheckGoogleProjectHasMoreBindingsThan(pid, 0),
				),
			},
		},
	})
}

func testAccCheckGoogleProjectExists(r, pid string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[r]
		if !ok {
			return fmt.Errorf("Not found: %s", r)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No ID is set")
		}

		if rs.Primary.ID != pid {
			return fmt.Errorf("Expected project %q to match ID %q in state", pid, rs.Primary.ID)
		}

		return nil
	}
}

func testAccCheckGoogleProjectHasBillingAccount(r, pid, billingId string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[r]
		if !ok {
			return fmt.Errorf("Not found: %s", r)
		}

		// State should match expected
		if rs.Primary.Attributes["billing_account"] != billingId {
			return fmt.Errorf("Billing ID in state (%s) does not match expected value (%s)", rs.Primary.Attributes["billing_account"], billingId)
		}

		// Actual value in API should match state and expected
		// Read the billing account
		config := testAccProvider.Meta().(*Config)
		ba, err := config.clientBilling.Projects.GetBillingInfo(prefixedProject(pid)).Do()
		if err != nil {
			return fmt.Errorf("Error reading billing account for project %q: %v", prefixedProject(pid), err)
		}
		if billingId != strings.TrimPrefix(ba.BillingAccountName, "billingAccounts/") {
			return fmt.Errorf("Billing ID returned by API (%s) did not match expected value (%s)", ba.BillingAccountName, billingId)
		}
		return nil
	}
}

func testAccCheckGoogleProjectHasMoreBindingsThan(pid string, count int) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		policy, err := getProjectIamPolicy(pid, testAccProvider.Meta().(*Config))
		if err != nil {
			return err
		}
		if len(policy.Bindings) <= count {
			return fmt.Errorf("Expected more than %d bindings, got %d: %#v", count, len(policy.Bindings), policy.Bindings)
		}
		return nil
	}
}

func testAccGoogleProject_toMerge(pid, name, org string) string {
	return fmt.Sprintf(`
resource "google_project" "acceptance" {
    project_id = "%s"
    name = "%s"
    org_id = "%s"
}

resource "google_project_iam_policy" "acceptance" {
    project = "${google_project.acceptance.project_id}"
    policy_data = "${data.google_iam_policy.acceptance.policy_data}"
}

data "google_iam_policy" "acceptance" {
    binding {
        role = "roles/storage.objectViewer"
	members = [
	  "user:evanbrown@google.com",
	]
    }
}`, pid, name, org)
}

func testAccGoogleProject_mergeEmpty(pid, name, org string) string {
	return fmt.Sprintf(`
resource "google_project" "acceptance" {
    project_id = "%s"
    name = "%s"
    org_id = "%s"
}`, pid, name, org)
}

func skipIfEnvNotSet(t *testing.T, envs ...string) {
	for _, k := range envs {
		if os.Getenv(k) == "" {
			t.Skipf("Environment variable %s is not set", k)
		}
	}
}
<|endoftext|>


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

package yall

import (
    "os"
)

func bindLambdaList(env *Env, lambdaList *Cell, args *Cell) {
    for c := lambdaList; c != Empty; c = c.cdr {
        e := c.car
        if symbol, ok := e.(*Symbol); ok {
            if symbol.name == "." {  // &rest (&body)
                env.Intern(c.Cadr().(*Symbol), args)
                break
            } else {
                expr := args.Car()
                env.Intern(symbol, expr)
                args = args.Cdr()
            }
        } else if cell, ok := e.(*Cell); ok {
            symbol := cell.car.(*Symbol)
            if Empty == args {
                defaultValue := cell.Cadr()
                env.Intern(symbol, defaultValue)
            } else {
                expr := args.Car()
                env.Intern(symbol, expr)
                args = args.Cdr()
            }
        }
    }
}

func Lambda(env *Env, args *Cell) Expr {
    lambdaList := args.Car().(*Cell)
    body := args.Cdr()
    return NewFunction("#lambda", func(args *Cell) Expr {
        derived := env.Derive()
        bindLambdaList(derived, lambdaList, args)
        return derived.Begin(body)
    })
}

var specialForms = map[string]func(*Env, *Cell) Expr{

    "lambda": Lambda,
    "fn":     Lambda,

    "def": func(env *Env, args *Cell) Expr {
        if symbol, ok := args.Car().(*Symbol); ok {
            value := env.Eval(args.Cadr())
            if function, ok := value.(*Function); ok {
                function.SetName(symbol.Name())
            }
            env.Intern(symbol, value)
            return symbol
        } else if cell, ok := args.Car().(*Cell); ok {
            symbol := cell.Car().(*Symbol)
            lambdaArgs := cell.Cdr()
            lambdaBody := args.Cdr()
            lambda := Lambda(env, NewCell(lambdaArgs, lambdaBody)).(*Function)
            lambda.SetName(symbol.Name())
            env.Intern(symbol, lambda)
            return symbol
        }
        panic(NewRuntimeError("Can't define"))
    },

    "macro": func(env *Env, args *Cell) Expr {
        lambdaList := args.Car().(*Cell)
        body := args.Cdr()
        return NewMacro("#macro", func(args *Cell) Expr {
            derived := env.Derive()
            bindLambdaList(derived, lambdaList, args)
            return derived.Begin(body)
        })
    },

    "if": func(env *Env, args *Cell) Expr {
        condition := env.Eval(args.Car())
        if condition != False {
            return env.Eval(args.Cadr())
        }
        return env.Eval(args.Caddr())
    },

    "inc!": func(env *Env, args *Cell) Expr {
        symbol, ok := args.Car().(*Symbol)
        if !ok {
            panic(NewRuntimeError("inc! requires a symbol"))
        }
        integer, ok := env.Eval(symbol).(*Integer)
        integer.setValue(integer.Value() + 1)
        return integer
    },

    "load": func(env *Env, args *Cell) Expr {
        args.Each(func(expr Expr) {
            if filename, ok := expr.(*String); ok {
                file, err := os.Open(filename.value)
                if nil != err {
                    panic(NewRuntimeError("Cannot load: " + filename.String()))
                }
                defer file.Close()
                env.Load(file)
            } else {
                panic(NewRuntimeError("Cannot load: " + expr.String()))
            }
        })
        return True
    },
}
<commit_msg>gofmt<commit_after>// Copyright 2012 Yuichi Araki. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package yall

import (
    "os"
)

func bindLambdaList(env *Env, lambdaList *Cell, args *Cell) {
    for c := lambdaList; c != Empty; c = c.cdr {
        e := c.car
        if symbol, ok := e.(*Symbol); ok {
            if symbol.name == "." { // &rest (&body)
                env.Intern(c.Cadr().(*Symbol), args)
                break
            } else {
                expr := args.Car()
                env.Intern(symbol, expr)
                args = args.Cdr()
            }
        } else if cell, ok := e.(*Cell); ok {
            symbol := cell.car.(*Symbol)
            if Empty == args {
                defaultValue := cell.Cadr()
                env.Intern(symbol, defaultValue)
            } else {
                expr := args.Car()
                env.Intern(symbol, expr)
                args = args.Cdr()
            }
        }
    }
}

func Lambda(env *Env, args *Cell) Expr {
    lambdaList := args.Car().(*Cell)
    body := args.Cdr()
    return NewFunction("#lambda", func(args *Cell) Expr {
        derived := env.Derive()
        bindLambdaList(derived, lambdaList, args)
        return derived.Begin(body)
    })
}

var specialForms = map[string]func(*Env, *Cell) Expr{

    "lambda": Lambda,
    "fn":     Lambda,

    "def": func(env *Env, args *Cell) Expr {
        if symbol, ok := args.Car().(*Symbol); ok {
            value := env.Eval(args.Cadr())
            if function, ok := value.(*Function); ok {
                function.SetName(symbol.Name())
            }
            env.Intern(symbol, value)
            return symbol
        } else if cell, ok := args.Car().(*Cell); ok {
            symbol := cell.Car().(*Symbol)
            lambdaArgs := cell.Cdr()
            lambdaBody := args.Cdr()
            lambda := Lambda(env, NewCell(lambdaArgs, lambdaBody)).(*Function)
            lambda.SetName(symbol.Name())
            env.Intern(symbol, lambda)
            return symbol
        }
        panic(NewRuntimeError("Can't define"))
    },

    "macro": func(env *Env, args *Cell) Expr {
        lambdaList := args.Car().(*Cell)
        body := args.Cdr()
        return NewMacro("#macro", func(args *Cell) Expr {
            derived := env.Derive()
            bindLambdaList(derived, lambdaList, args)
            return derived.Begin(body)
        })
    },

    "if": func(env *Env, args *Cell) Expr {
        condition := env.Eval(args.Car())
        if condition != False {
            return env.Eval(args.Cadr())
        }
        return env.Eval(args.Caddr())
    },

    "inc!": func(env *Env, args *Cell) Expr {
        symbol, ok := args.Car().(*Symbol)
        if !ok {
            panic(NewRuntimeError("inc! requires a symbol"))
        }
        integer, ok := env.Eval(symbol).(*Integer)
        integer.setValue(integer.Value() + 1)
        return integer
    },

    "load": func(env *Env, args *Cell) Expr {
        args.Each(func(expr Expr) {
            if filename, ok := expr.(*String); ok {
                file, err := os.Open(filename.value)
                if nil != err {
                    panic(NewRuntimeError("Cannot load: " + filename.String()))
                }
                defer file.Close()
                env.Load(file)
            } else {
                panic(NewRuntimeError("Cannot load: " + expr.String()))
            }
        })
        return True
    },
}
<|endoftext|>


<commit_before>/*
Copyright 2020 The Jetstack cert-manager contributors.

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 certificaterequest

import (
	"context"
	"encoding/pem"
	"errors"
	"fmt"
	"io/ioutil"
	"k8s.io/apimachinery/pkg/util/wait"
	"time"

	"github.com/spf13/cobra"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/cli-runtime/pkg/genericclioptions"
	"k8s.io/cli-runtime/pkg/resource"
	restclient "k8s.io/client-go/rest"
	cmdutil "k8s.io/kubectl/pkg/cmd/util"
	"k8s.io/kubectl/pkg/util/i18n"
	"k8s.io/kubectl/pkg/util/templates"

	"github.com/jetstack/cert-manager/cmd/ctl/pkg/util"
	apiutil "github.com/jetstack/cert-manager/pkg/api/util"
	cmapiv1alpha2 "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
	cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
	cmclient "github.com/jetstack/cert-manager/pkg/client/clientset/versioned"
	"github.com/jetstack/cert-manager/pkg/ctl"
	"github.com/jetstack/cert-manager/pkg/util/pki"
)

var (
	long = templates.LongDesc(i18n.T(`
Create a new CertificateRequest resource based on a Certificate resource, by generating a private key locally and create a 'certificate signing request' to be submitted to a cert-manager Issuer.`))

	example = templates.Examples(i18n.T(`
# Create a CertificateRequest with the name 'my-cr', saving the private key in a file named 'my-cr.key'.
kubectl cert-manager create certificaterequest my-cr --from-certificate-file my-certificate.yaml

# Create a CertificateRequest in namespace default, provided no conflict with namespace defined in file.
kubectl cert-manager create certificaterequest my-cr --namespace default --from-certificate-file my-certificate.yaml

# Create a CertificateRequest and store private key in file 'new.key'.
kubectl cert-manager create certificaterequest my-cr --from-certificate-file my-certificate.yaml --output-key-file new.key

# Create a CertificateRequest, wait for it to be signed for up to 5 minutes (default) and store the x509 certificate in file 'new.crt'.
kubectl cert-manager create certificaterequest my-cr --from-certificate-file my-certificate.yaml --fetch-certificate --output-cert-file new.crt

# Create a CertificateRequest, wait for it to be signed for up to 20 minutes and store the x509 certificate in file 'my-cr.crt'.
kubectl cert-manager create certificaterequest my-cr --from-certificate-file my-certificate.yaml --fetch-certificate --timeout 20m
`))
)

var (
	// Dedicated scheme used by the ctl tool that has the internal cert-manager types,
	// and their conversion functions registered
	scheme = ctl.Scheme
)

// Options is a struct to support create certificaterequest command
type Options struct {
	CMClient   cmclient.Interface
	RESTConfig *restclient.Config
	// Namespace resulting from the merged result of all overrides
	// since namespace can be specified in file, as flag and in kube config
	CmdNamespace string
	// boolean indicating if there was an Override in determining CmdNamespace
	EnforceNamespace bool
	// Name of file that the generated private key will be stored in
	// If not specified, the private key will be written to <NameOfCR>.key
	KeyFilename string
	// If true, will wait for CertificateRequest to be ready to store the x509 certificate in a file
	// Command will block until CertificateRequest is ready or timeout as specified by Timeout happens
	FetchCert bool
	// Name of file that the generated x509 certificate will be stored in if --fetch-certificate flag is set
	// If not specified, the private key will be written to <NameOfCR>.crt
	CertFileName string
	// Path to a file containing a Certificate resource used as a template
	// when generating the CertificateRequest resource
	// Required
	InputFilename string
	// Length of time the command blocks to wait on CertificateRequest to be ready if --fetch-certificate flag is set
	// If not specified, default value is 5 minutes
	Timeout time.Duration

	genericclioptions.IOStreams
}

// NewOptions returns initialized Options
func NewOptions(ioStreams genericclioptions.IOStreams) *Options {
	return &Options{
		IOStreams: ioStreams,
	}
}

// NewCmdCreateCR returns a cobra command for create CertificateRequest
func NewCmdCreateCR(ioStreams genericclioptions.IOStreams, factory cmdutil.Factory) *cobra.Command {
	o := NewOptions(ioStreams)
	cmd := &cobra.Command{
		Use:     "certificaterequest",
		Aliases: []string{"cr"},
		Short:   "Create a cert-manager CertificateRequest resource, using a Certificate resource as a template",
		Long:    long,
		Example: example,
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(o.Validate(args))
			cmdutil.CheckErr(o.Complete(factory))
			cmdutil.CheckErr(o.Run(args))
		},
	}
	cmd.Flags().StringVar(&o.InputFilename, "from-certificate-file", o.InputFilename,
		"Path to a file containing a Certificate resource used as a template when generating the CertificateRequest resource")
	cmd.Flags().StringVar(&o.KeyFilename, "output-key-file", o.KeyFilename,
		"Name of file that the generated private key will be written to")
	cmd.Flags().StringVar(&o.CertFileName, "output-certificate-file", o.CertFileName,
		"Name of the file the certificate is to be stored in")
	cmd.Flags().BoolVar(&o.FetchCert, "fetch-certificate", o.FetchCert,
		"If set to true, command will wait for CertificateRequest to be signed to store x509 certificate in a file")
	cmd.Flags().DurationVar(&o.Timeout, "timeout", 5*time.Minute,
		"Time before timeout when waiting for CertificateRequest to be signed, must include unit, e.g. 10m or 1h")

	return cmd
}

// Validate validates the provided options
func (o *Options) Validate(args []string) error {
	if len(args) < 1 {
		return errors.New("the name of the CertificateRequest to be created has to be provided as argument")
	}
	if len(args) > 1 {
		return errors.New("only one argument can be passed in: the name of the CertificateRequest")
	}

	if o.KeyFilename != "" && o.CertFileName != "" && o.KeyFilename == o.CertFileName {
		return errors.New("the file to store private key cannot be the same as the file to store certificate")
	}

	if !o.FetchCert && o.CertFileName != "" {
		return errors.New("cannot specify file to store certificate if not waiting for and fetching certificate, please set --fetch-certificate flag")
	}

	return nil
}

// Complete takes the command arguments and factory and infers any remaining options.
func (o *Options) Complete(f cmdutil.Factory) error {
	var err error

	o.CmdNamespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
	if err != nil {
		return err
	}

	o.RESTConfig, err = f.ToRESTConfig()
	if err != nil {
		return err
	}

	o.CMClient, err = cmclient.NewForConfig(o.RESTConfig)
	if err != nil {
		return err
	}

	return nil
}

// Run executes create certificaterequest command
func (o *Options) Run(args []string) error {
	builder := new(resource.Builder)

	// Read file as internal API version
	r := builder.
		WithScheme(scheme, schema.GroupVersion{Group: cmapiv1alpha2.SchemeGroupVersion.Group, Version: runtime.APIVersionInternal}).
		LocalParam(true).ContinueOnError().
		NamespaceParam(o.CmdNamespace).DefaultNamespace().
		FilenameParam(o.EnforceNamespace, &resource.FilenameOptions{Filenames: []string{o.InputFilename}}).Flatten().Do()

	if err := r.Err(); err != nil {
		return fmt.Errorf("error when getting Result from Builder: %s", err)
	}

	singleItemImplied := false
	infos, err := r.IntoSingleItemImplied(&singleItemImplied).Infos()
	if err != nil {
		return fmt.Errorf("error when getting infos out of Result: %s", err)
	}

	// Ensure only one object per command
	if len(infos) == 0 {
		return fmt.Errorf("no objects found in manifest file %q. Expected one Certificate object", o.InputFilename)
	}
	if len(infos) > 1 {
		return fmt.Errorf("multiple objects found in manifest file %q. Expected only one Certificate object", o.InputFilename)
	}
	info := infos[0]
	// Convert to v1alpha2 because that version is needed for functions that follow
	crtObj, err := scheme.ConvertToVersion(info.Object, cmapiv1alpha2.SchemeGroupVersion)
	if err != nil {
		return fmt.Errorf("failed to convert object into version v1alpha2: %w", err)
	}

	// Cast Object into Certificate
	crt, ok := crtObj.(*cmapiv1alpha2.Certificate)
	if !ok {
		return errors.New("decoded object is not a v1alpha2 Certificate")
	}

	signer, err := pki.GeneratePrivateKeyForCertificate(crt)
	if err != nil {
		return fmt.Errorf("error when generating new private key for CertificateRequest: %w", err)
	}

	keyData, err := pki.EncodePrivateKey(signer, crt.Spec.KeyEncoding)
	if err != nil {
		return fmt.Errorf("failed to encode new private key for CertificateRequest: %w", err)
	}

	crName := args[0]

	// Storing private key to file
	keyFileName := crName + ".key"
	if o.KeyFilename != "" {
		keyFileName = o.KeyFilename
	}
	if err := ioutil.WriteFile(keyFileName, keyData, 0600); err != nil {
		return fmt.Errorf("error when writing private key to file: %w", err)
	}
	fmt.Fprintf(o.Out, "Private key written to file %s\n", keyFileName)

	// Build CertificateRequest with name as specified by argument
	req, err := buildCertificateRequest(crt, keyData, crName)
	if err != nil {
		return fmt.Errorf("error when building CertificateRequest: %w", err)
	}

	ns := crt.Namespace
	if ns == "" {
		ns = o.CmdNamespace
	}
	req, err = o.CMClient.CertmanagerV1alpha2().CertificateRequests(ns).Create(context.TODO(), req, metav1.CreateOptions{})
	if err != nil {
		return fmt.Errorf("error creating CertificateRequest: %w", err)
	}
	fmt.Fprintf(o.Out, "CertificateRequest %s has been created in namespace %s\n", req.Name, req.Namespace)

	if o.FetchCert {
		fmt.Fprintf(o.Out, "CertificateRequest %v in namespace %v has not been signed yet. Wait until it is signed...\n",
			req.Name, req.Namespace)
		err = wait.Poll(time.Second, o.Timeout, func() (done bool, err error) {
			req, err := o.CMClient.CertmanagerV1alpha2().CertificateRequests(req.Namespace).Get(context.TODO(), req.Name, metav1.GetOptions{})
			if err != nil {
				return false, nil
			}
			return apiutil.CertificateRequestHasCondition(req, cmapiv1alpha2.CertificateRequestCondition{
				Type:   cmapiv1alpha2.CertificateRequestConditionReady,
				Status: cmmeta.ConditionTrue,
			}), nil
		})
		if err != nil {
			return fmt.Errorf("error when waiting for CertificateRequest to be signed: %w", err)
		}
		fmt.Fprintf(o.Out, "CertificateRequest %v in namespace %v has been signed\n", req.Name, req.Namespace)

		// Fetch x509 certificate and store to file
		actualCertFileName := req.Name + ".crt"
		if o.CertFileName != "" {
			actualCertFileName = o.CertFileName
		}
		err = util.FetchCertificateFromCR(o.CMClient, req.Name, req.Namespace, actualCertFileName, o.IOStreams)
		if err != nil {
			return fmt.Errorf("error when writing certificate to file: %w", err)
		}
		fmt.Fprintf(o.Out, "Certificate written to file %s\n", actualCertFileName)
	}

	return nil
}

// Builds a CertificateRequest
func buildCertificateRequest(crt *cmapiv1alpha2.Certificate, pk []byte, crName string) (*cmapiv1alpha2.CertificateRequest, error) {
	csrPEM, err := generateCSR(crt, pk)
	if err != nil {
		return nil, err
	}

	cr := &cmapiv1alpha2.CertificateRequest{
		ObjectMeta: metav1.ObjectMeta{
			Name:        crName,
			Annotations: crt.Annotations,
			Labels:      crt.Labels,
		},
		Spec: cmapiv1alpha2.CertificateRequestSpec{
			CSRPEM:    csrPEM,
			Duration:  crt.Spec.Duration,
			IssuerRef: crt.Spec.IssuerRef,
			IsCA:      crt.Spec.IsCA,
			Usages:    crt.Spec.Usages,
		},
	}

	return cr, nil
}

func generateCSR(crt *cmapiv1alpha2.Certificate, pk []byte) ([]byte, error) {
	csr, err := pki.GenerateCSR(crt)
	if err != nil {
		return nil, err
	}

	signer, err := pki.DecodePrivateKeyBytes(pk)
	if err != nil {
		return nil, err
	}

	csrDER, err := pki.EncodeCSR(csr, signer)
	if err != nil {
		return nil, err
	}

	csrPEM := pem.EncodeToMemory(&pem.Block{
		Type: "CERTIFICATE REQUEST", Bytes: csrDER,
	})

	return csrPEM, nil
}
<commit_msg>Error when Ready condition is true but status.certificate is empty<commit_after>/*
Copyright 2020 The Jetstack cert-manager contributors.

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 certificaterequest

import (
	"context"
	"encoding/pem"
	"errors"
	"fmt"
	"io/ioutil"
	"k8s.io/apimachinery/pkg/util/wait"
	"time"

	"github.com/spf13/cobra"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/cli-runtime/pkg/genericclioptions"
	"k8s.io/cli-runtime/pkg/resource"
	restclient "k8s.io/client-go/rest"
	cmdutil "k8s.io/kubectl/pkg/cmd/util"
	"k8s.io/kubectl/pkg/util/i18n"
	"k8s.io/kubectl/pkg/util/templates"

	"github.com/jetstack/cert-manager/cmd/ctl/pkg/util"
	apiutil "github.com/jetstack/cert-manager/pkg/api/util"
	cmapiv1alpha2 "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1alpha2"
	cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
	cmclient "github.com/jetstack/cert-manager/pkg/client/clientset/versioned"
	"github.com/jetstack/cert-manager/pkg/ctl"
	"github.com/jetstack/cert-manager/pkg/util/pki"
)

var (
	long = templates.LongDesc(i18n.T(`
Create a new CertificateRequest resource based on a Certificate resource, by generating a private key locally and create a 'certificate signing request' to be submitted to a cert-manager Issuer.`))

	example = templates.Examples(i18n.T(`
# Create a CertificateRequest with the name 'my-cr', saving the private key in a file named 'my-cr.key'.
kubectl cert-manager create certificaterequest my-cr --from-certificate-file my-certificate.yaml

# Create a CertificateRequest in namespace default, provided no conflict with namespace defined in file.
kubectl cert-manager create certificaterequest my-cr --namespace default --from-certificate-file my-certificate.yaml

# Create a CertificateRequest and store private key in file 'new.key'.
kubectl cert-manager create certificaterequest my-cr --from-certificate-file my-certificate.yaml --output-key-file new.key

# Create a CertificateRequest, wait for it to be signed for up to 5 minutes (default) and store the x509 certificate in file 'new.crt'.
kubectl cert-manager create certificaterequest my-cr --from-certificate-file my-certificate.yaml --fetch-certificate --output-cert-file new.crt

# Create a CertificateRequest, wait for it to be signed for up to 20 minutes and store the x509 certificate in file 'my-cr.crt'.
kubectl cert-manager create certificaterequest my-cr --from-certificate-file my-certificate.yaml --fetch-certificate --timeout 20m
`))
)

var (
	// Dedicated scheme used by the ctl tool that has the internal cert-manager types,
	// and their conversion functions registered
	scheme = ctl.Scheme
)

// Options is a struct to support create certificaterequest command
type Options struct {
	CMClient   cmclient.Interface
	RESTConfig *restclient.Config
	// Namespace resulting from the merged result of all overrides
	// since namespace can be specified in file, as flag and in kube config
	CmdNamespace string
	// boolean indicating if there was an Override in determining CmdNamespace
	EnforceNamespace bool
	// Name of file that the generated private key will be stored in
	// If not specified, the private key will be written to <NameOfCR>.key
	KeyFilename string
	// If true, will wait for CertificateRequest to be ready to store the x509 certificate in a file
	// Command will block until CertificateRequest is ready or timeout as specified by Timeout happens
	FetchCert bool
	// Name of file that the generated x509 certificate will be stored in if --fetch-certificate flag is set
	// If not specified, the private key will be written to <NameOfCR>.crt
	CertFileName string
	// Path to a file containing a Certificate resource used as a template
	// when generating the CertificateRequest resource
	// Required
	InputFilename string
	// Length of time the command blocks to wait on CertificateRequest to be ready if --fetch-certificate flag is set
	// If not specified, default value is 5 minutes
	Timeout time.Duration

	genericclioptions.IOStreams
}

// NewOptions returns initialized Options
func NewOptions(ioStreams genericclioptions.IOStreams) *Options {
	return &Options{
		IOStreams: ioStreams,
	}
}

// NewCmdCreateCR returns a cobra command for create CertificateRequest
func NewCmdCreateCR(ioStreams genericclioptions.IOStreams, factory cmdutil.Factory) *cobra.Command {
	o := NewOptions(ioStreams)
	cmd := &cobra.Command{
		Use:     "certificaterequest",
		Aliases: []string{"cr"},
		Short:   "Create a cert-manager CertificateRequest resource, using a Certificate resource as a template",
		Long:    long,
		Example: example,
		Run: func(cmd *cobra.Command, args []string) {
			cmdutil.CheckErr(o.Validate(args))
			cmdutil.CheckErr(o.Complete(factory))
			cmdutil.CheckErr(o.Run(args))
		},
	}
	cmd.Flags().StringVar(&o.InputFilename, "from-certificate-file", o.InputFilename,
		"Path to a file containing a Certificate resource used as a template when generating the CertificateRequest resource")
	cmd.Flags().StringVar(&o.KeyFilename, "output-key-file", o.KeyFilename,
		"Name of file that the generated private key will be written to")
	cmd.Flags().StringVar(&o.CertFileName, "output-certificate-file", o.CertFileName,
		"Name of the file the certificate is to be stored in")
	cmd.Flags().BoolVar(&o.FetchCert, "fetch-certificate", o.FetchCert,
		"If set to true, command will wait for CertificateRequest to be signed to store x509 certificate in a file")
	cmd.Flags().DurationVar(&o.Timeout, "timeout", 5*time.Minute,
		"Time before timeout when waiting for CertificateRequest to be signed, must include unit, e.g. 10m or 1h")

	return cmd
}

// Validate validates the provided options
func (o *Options) Validate(args []string) error {
	if len(args) < 1 {
		return errors.New("the name of the CertificateRequest to be created has to be provided as argument")
	}
	if len(args) > 1 {
		return errors.New("only one argument can be passed in: the name of the CertificateRequest")
	}

	if o.KeyFilename != "" && o.CertFileName != "" && o.KeyFilename == o.CertFileName {
		return errors.New("the file to store private key cannot be the same as the file to store certificate")
	}

	if !o.FetchCert && o.CertFileName != "" {
		return errors.New("cannot specify file to store certificate if not waiting for and fetching certificate, please set --fetch-certificate flag")
	}

	return nil
}

// Complete takes the command arguments and factory and infers any remaining options.
func (o *Options) Complete(f cmdutil.Factory) error {
	var err error

	o.CmdNamespace, o.EnforceNamespace, err = f.ToRawKubeConfigLoader().Namespace()
	if err != nil {
		return err
	}

	o.RESTConfig, err = f.ToRESTConfig()
	if err != nil {
		return err
	}

	o.CMClient, err = cmclient.NewForConfig(o.RESTConfig)
	if err != nil {
		return err
	}

	return nil
}

// Run executes create certificaterequest command
func (o *Options) Run(args []string) error {
	builder := new(resource.Builder)

	// Read file as internal API version
	r := builder.
		WithScheme(scheme, schema.GroupVersion{Group: cmapiv1alpha2.SchemeGroupVersion.Group, Version: runtime.APIVersionInternal}).
		LocalParam(true).ContinueOnError().
		NamespaceParam(o.CmdNamespace).DefaultNamespace().
		FilenameParam(o.EnforceNamespace, &resource.FilenameOptions{Filenames: []string{o.InputFilename}}).Flatten().Do()

	if err := r.Err(); err != nil {
		return fmt.Errorf("error when getting Result from Builder: %s", err)
	}

	singleItemImplied := false
	infos, err := r.IntoSingleItemImplied(&singleItemImplied).Infos()
	if err != nil {
		return fmt.Errorf("error when getting infos out of Result: %s", err)
	}

	// Ensure only one object per command
	if len(infos) == 0 {
		return fmt.Errorf("no objects found in manifest file %q. Expected one Certificate object", o.InputFilename)
	}
	if len(infos) > 1 {
		return fmt.Errorf("multiple objects found in manifest file %q. Expected only one Certificate object", o.InputFilename)
	}
	info := infos[0]
	// Convert to v1alpha2 because that version is needed for functions that follow
	crtObj, err := scheme.ConvertToVersion(info.Object, cmapiv1alpha2.SchemeGroupVersion)
	if err != nil {
		return fmt.Errorf("failed to convert object into version v1alpha2: %w", err)
	}

	// Cast Object into Certificate
	crt, ok := crtObj.(*cmapiv1alpha2.Certificate)
	if !ok {
		return errors.New("decoded object is not a v1alpha2 Certificate")
	}

	signer, err := pki.GeneratePrivateKeyForCertificate(crt)
	if err != nil {
		return fmt.Errorf("error when generating new private key for CertificateRequest: %w", err)
	}

	keyData, err := pki.EncodePrivateKey(signer, crt.Spec.KeyEncoding)
	if err != nil {
		return fmt.Errorf("failed to encode new private key for CertificateRequest: %w", err)
	}

	crName := args[0]

	// Storing private key to file
	keyFileName := crName + ".key"
	if o.KeyFilename != "" {
		keyFileName = o.KeyFilename
	}
	if err := ioutil.WriteFile(keyFileName, keyData, 0600); err != nil {
		return fmt.Errorf("error when writing private key to file: %w", err)
	}
	fmt.Fprintf(o.Out, "Private key written to file %s\n", keyFileName)

	// Build CertificateRequest with name as specified by argument
	req, err := buildCertificateRequest(crt, keyData, crName)
	if err != nil {
		return fmt.Errorf("error when building CertificateRequest: %w", err)
	}

	ns := crt.Namespace
	if ns == "" {
		ns = o.CmdNamespace
	}
	req, err = o.CMClient.CertmanagerV1alpha2().CertificateRequests(ns).Create(context.TODO(), req, metav1.CreateOptions{})
	if err != nil {
		return fmt.Errorf("error creating CertificateRequest: %w", err)
	}
	fmt.Fprintf(o.Out, "CertificateRequest %s has been created in namespace %s\n", req.Name, req.Namespace)

	if o.FetchCert {
		fmt.Fprintf(o.Out, "CertificateRequest %v in namespace %v has not been signed yet. Wait until it is signed...\n",
			req.Name, req.Namespace)
		err = wait.Poll(time.Second, o.Timeout, func() (done bool, err error) {
			req, err := o.CMClient.CertmanagerV1alpha2().CertificateRequests(req.Namespace).Get(context.TODO(), req.Name, metav1.GetOptions{})
			if err != nil {
				return false, nil
			}
			return apiutil.CertificateRequestHasCondition(req, cmapiv1alpha2.CertificateRequestCondition{
				Type:   cmapiv1alpha2.CertificateRequestConditionReady,
				Status: cmmeta.ConditionTrue,
			}), nil
		})
		if err != nil {
			return fmt.Errorf("error when waiting for CertificateRequest to be signed: %w", err)
		}
		if len(req.Status.Certificate) == 0 {
			fmt.Fprintf(o.Out, "%s\n", req.Status.Certificate)
			return errors.New("CertificateRequest in invalid state: Ready Condition is set but status.certificate is empty")
		}
		fmt.Fprintf(o.Out, "CertificateRequest %v in namespace %v has been signed\n", req.Name, req.Namespace)

		// Fetch x509 certificate and store to file
		actualCertFileName := req.Name + ".crt"
		if o.CertFileName != "" {
			actualCertFileName = o.CertFileName
		}
		err = util.FetchCertificateFromCR(o.CMClient, req.Name, req.Namespace, actualCertFileName, o.IOStreams)
		if err != nil {
			return fmt.Errorf("error when writing certificate to file: %w", err)
		}
		fmt.Fprintf(o.Out, "Certificate written to file %s\n", actualCertFileName)
	}

	return nil
}

// Builds a CertificateRequest
func buildCertificateRequest(crt *cmapiv1alpha2.Certificate, pk []byte, crName string) (*cmapiv1alpha2.CertificateRequest, error) {
	csrPEM, err := generateCSR(crt, pk)
	if err != nil {
		return nil, err
	}

	cr := &cmapiv1alpha2.CertificateRequest{
		ObjectMeta: metav1.ObjectMeta{
			Name:        crName,
			Annotations: crt.Annotations,
			Labels:      crt.Labels,
		},
		Spec: cmapiv1alpha2.CertificateRequestSpec{
			CSRPEM:    csrPEM,
			Duration:  crt.Spec.Duration,
			IssuerRef: crt.Spec.IssuerRef,
			IsCA:      crt.Spec.IsCA,
			Usages:    crt.Spec.Usages,
		},
	}

	return cr, nil
}

func generateCSR(crt *cmapiv1alpha2.Certificate, pk []byte) ([]byte, error) {
	csr, err := pki.GenerateCSR(crt)
	if err != nil {
		return nil, err
	}

	signer, err := pki.DecodePrivateKeyBytes(pk)
	if err != nil {
		return nil, err
	}

	csrDER, err := pki.EncodeCSR(csr, signer)
	if err != nil {
		return nil, err
	}

	csrPEM := pem.EncodeToMemory(&pem.Block{
		Type: "CERTIFICATE REQUEST", Bytes: csrDER,
	})

	return csrPEM, nil
}
<|endoftext|>


<commit_before>package gowebdav

import (
	"fmt"
	"io"
	"net/http"
	"strings"
)

func (c *Client) req(method, path string, body io.Reader, intercept func(*http.Request)) (req *http.Response, err error) {
	r, err := http.NewRequest(method, PathEscape(Join(c.root, path)), body)
	if err != nil {
		return nil, err
	}

	for k, vals := range c.headers {
		for _, v := range vals {
			r.Header.Add(k, v)
		}
	}

	c.auth.Authorize(c, method, path)

	if intercept != nil {
		intercept(r)
	}

	return c.c.Do(r)
}

func (c *Client) mkcol(path string) int {
	rs, err := c.req("MKCOL", path, nil, nil)
	defer rs.Body.Close()
	if err != nil {
		return 400
	}

	if rs.StatusCode == 201 || rs.StatusCode == 405 {
		return 201
	}

	return rs.StatusCode
}

func (c *Client) options(path string) (*http.Response, error) {
	return c.req("OPTIONS", path, nil, func(rq *http.Request) {
		rq.Header.Add("Depth", "0")
	})
}

func (c *Client) propfind(path string, self bool, body string, resp interface{}, parse func(resp interface{}) error) error {
	rs, err := c.req("PROPFIND", path, strings.NewReader(body), func(rq *http.Request) {
		if self {
			rq.Header.Add("Depth", "0")
		} else {
			rq.Header.Add("Depth", "1")
		}
		rq.Header.Add("Content-Type", "text/xml;charset=UTF-8")
		rq.Header.Add("Accept", "application/xml,text/xml")
		rq.Header.Add("Accept-Charset", "utf-8")
		// TODO add support for 'gzip,deflate;q=0.8,q=0.7'
		rq.Header.Add("Accept-Encoding", "")
	})
	defer rs.Body.Close()
	if err != nil {
		return err
	}

	if rs.StatusCode != 207 {
		return fmt.Errorf("%s - %s %s", rs.Status, "PROPFIND", path)
	}

	return parseXML(rs.Body, resp, parse)
}

func (c *Client) doCopyMove(method string, oldpath string, newpath string, overwrite bool) (int, io.ReadCloser) {
	rs, err := c.req(method, oldpath, nil, func(rq *http.Request) {
		rq.Header.Add("Destination", Join(c.root, newpath))
		if overwrite {
			rq.Header.Add("Overwrite", "T")
		} else {
			rq.Header.Add("Overwrite", "F")
		}
	})
	if err != nil {
		return 400, nil
	}
	return rs.StatusCode, rs.Body
}

func (c *Client) copymove(method string, oldpath string, newpath string, overwrite bool) error {
	s, data := c.doCopyMove(method, oldpath, newpath, overwrite)
	defer data.Close()

	switch s {
	case 201, 204:
		return nil

	case 207:
		// TODO handle multistat errors, worst case ...
		log(fmt.Sprintf(" TODO handle %s - %s multistatus result %s", method, oldpath, String(data)))

	case 409:
		// TODO create dst path
	}

	return newPathError(method, oldpath, s)
}

func (c *Client) put(path string, stream io.Reader) int {
	rs, err := c.req("PUT", path, stream, nil)
	defer rs.Body.Close()
	if err != nil {
		return 400
	}

	return rs.StatusCode
}
<commit_msg>fix authorization<commit_after>package gowebdav

import (
	"fmt"
	"io"
	"net/http"
	"strings"
)

func (c *Client) req(method, path string, body io.Reader, intercept func(*http.Request)) (req *http.Response, err error) {
	r, err := http.NewRequest(method, PathEscape(Join(c.root, path)), body)
	if err != nil {
		return nil, err
	}

	c.auth.Authorize(c, method, path)

	for k, vals := range c.headers {
		for _, v := range vals {
			r.Header.Add(k, v)
		}
	}

	if intercept != nil {
		intercept(r)
	}

	return c.c.Do(r)
}

func (c *Client) mkcol(path string) int {
	rs, err := c.req("MKCOL", path, nil, nil)
	defer rs.Body.Close()
	if err != nil {
		return 400
	}

	if rs.StatusCode == 201 || rs.StatusCode == 405 {
		return 201
	}

	return rs.StatusCode
}

func (c *Client) options(path string) (*http.Response, error) {
	return c.req("OPTIONS", path, nil, func(rq *http.Request) {
		rq.Header.Add("Depth", "0")
	})
}

func (c *Client) propfind(path string, self bool, body string, resp interface{}, parse func(resp interface{}) error) error {
	rs, err := c.req("PROPFIND", path, strings.NewReader(body), func(rq *http.Request) {
		if self {
			rq.Header.Add("Depth", "0")
		} else {
			rq.Header.Add("Depth", "1")
		}
		rq.Header.Add("Content-Type", "text/xml;charset=UTF-8")
		rq.Header.Add("Accept", "application/xml,text/xml")
		rq.Header.Add("Accept-Charset", "utf-8")
		// TODO add support for 'gzip,deflate;q=0.8,q=0.7'
		rq.Header.Add("Accept-Encoding", "")
	})
	defer rs.Body.Close()
	if err != nil {
		return err
	}

	if rs.StatusCode != 207 {
		return fmt.Errorf("%s - %s %s", rs.Status, "PROPFIND", path)
	}

	return parseXML(rs.Body, resp, parse)
}

func (c *Client) doCopyMove(method string, oldpath string, newpath string, overwrite bool) (int, io.ReadCloser) {
	rs, err := c.req(method, oldpath, nil, func(rq *http.Request) {
		rq.Header.Add("Destination", Join(c.root, newpath))
		if overwrite {
			rq.Header.Add("Overwrite", "T")
		} else {
			rq.Header.Add("Overwrite", "F")
		}
	})
	if err != nil {
		return 400, nil
	}
	return rs.StatusCode, rs.Body
}

func (c *Client) copymove(method string, oldpath string, newpath string, overwrite bool) error {
	s, data := c.doCopyMove(method, oldpath, newpath, overwrite)
	defer data.Close()

	switch s {
	case 201, 204:
		return nil

	case 207:
		// TODO handle multistat errors, worst case ...
		log(fmt.Sprintf(" TODO handle %s - %s multistatus result %s", method, oldpath, String(data)))

	case 409:
		// TODO create dst path
	}

	return newPathError(method, oldpath, s)
}

func (c *Client) put(path string, stream io.Reader) int {
	rs, err := c.req("PUT", path, stream, nil)
	defer rs.Body.Close()
	if err != nil {
		return 400
	}

	return rs.StatusCode
}
<|endoftext|>


<commit_before>package fire

import (
	"net/http"
	"reflect"
	"strings"

	"github.com/manyminds/api2go"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

// A Resource provides an interface to a model.
//
// Note: Resources must not be modified after adding to an Endpoint.
type Resource struct {
	// The model that this resource should provide (e.g. &Foo{}).
	Model Model

	// The Authorizer is run on all actions. Will return an Unauthorized status
	// if an user error is returned.
	Authorizer Callback

	// The Validator is run to validate Create, Update and Delete actions. Will
	// return a Bad Request status if an user error is returned.
	Validator Callback

	adapter  *adapter
	endpoint *Endpoint
}

/* api2go interface */

// InitializeObject implements the api2go.ObjectInitializer interface.
func (r *Resource) InitializeObject(obj interface{}) {
	// initialize model
	Init(obj.(Model))
}

// FindAll implements the api2go.FindAll interface.
func (r *Resource) FindAll(req api2go.Request) (api2go.Responder, error) {
	// build context
	ctx := r.buildContext(FindAll, &req)
	ctx.Query = bson.M{}

	// set relationship filters
	err := r.setRelationshipFilters(ctx)
	if err != nil {
		return nil, err
	}

	// add filters
	for _, field := range r.Model.Meta().FieldsByTag("filterable") {
		if values, ok := req.QueryParams["filter["+field.JSONName+"]"]; ok {
			ctx.Query[field.BSONName] = bson.M{"$in": values}
		}
	}

	// add sorting
	if sortParam, ok := req.QueryParams["sort"]; ok {
		for _, params := range sortParam {
			for _, field := range r.Model.Meta().FieldsByTag("sortable") {
				if params == field.BSONName || params == "-"+field.BSONName {
					ctx.Sorting = append(ctx.Sorting, params)
				}
			}
		}
	}

	// run authorizer if available
	if err := r.runCallback(r.Authorizer, ctx, http.StatusUnauthorized); err != nil {
		return nil, err
	}

	// prepare slice
	pointer := r.Model.Meta().MakeSlice()

	// query db
	err = r.endpoint.db.C(r.Model.Meta().Collection).Find(ctx.Query).Sort(ctx.Sorting...).All(pointer)
	if err != nil {
		return nil, api2go.NewHTTPError(err, "Error while retrieving resources", http.StatusInternalServerError)
	}

	// initialize slice
	InitSlice(pointer)

	// api2go needs a direct slice reference
	slice := reflect.ValueOf(pointer).Elem().Interface()

	return &api2go.Response{Res: slice, Code: http.StatusOK}, nil
}

// FindOne implements a part of the api2go.CRUD interface.
func (r *Resource) FindOne(id string, req api2go.Request) (api2go.Responder, error) {
	// validate id
	if !bson.IsObjectIdHex(id) {
		return nil, api2go.NewHTTPError(nil, "Invalid id", http.StatusBadRequest)
	}

	// build context
	ctx := r.buildContext(FindOne, &req)
	ctx.Query = bson.M{"_id": bson.ObjectIdHex(id)}

	// run authorizer if available
	if err := r.runCallback(r.Authorizer, ctx, http.StatusUnauthorized); err != nil {
		return nil, err
	}

	// prepare object
	obj := r.Model.Meta().Make()

	// query db
	err := r.endpoint.db.C(r.Model.Meta().Collection).Find(ctx.Query).One(obj)
	if err == mgo.ErrNotFound {
		return nil, api2go.NewHTTPError(err, "Resource not found", http.StatusNotFound)
	} else if err != nil {
		return nil, api2go.NewHTTPError(err, "Error while retrieving resource", http.StatusInternalServerError)
	}

	// initialize model
	model := Init(obj.(Model))

	return &api2go.Response{Res: model, Code: http.StatusOK}, nil
}

// Create implements a part of the api2go.CRUD interface.
func (r *Resource) Create(obj interface{}, req api2go.Request) (api2go.Responder, error) {
	// build context
	ctx := r.buildContext(Create, &req)
	ctx.Model = obj.(Model)

	// run authorizer if available
	if err := r.runCallback(r.Authorizer, ctx, http.StatusUnauthorized); err != nil {
		return nil, err
	}

	// validate model
	err := ctx.Model.Validate(true)
	if err != nil {
		return nil, api2go.NewHTTPError(nil, err.Error(), http.StatusBadRequest)
	}

	// run validator if available
	if err := r.runCallback(r.Validator, ctx, http.StatusBadRequest); err != nil {
		return nil, err
	}

	// query db
	err = r.endpoint.db.C(r.Model.Meta().Collection).Insert(ctx.Model)
	if err != nil {
		return nil, api2go.NewHTTPError(err, "Error while saving resource", http.StatusInternalServerError)
	}

	return &api2go.Response{Res: ctx.Model, Code: http.StatusCreated}, nil
}

// Update implements a part of the api2go.CRUD interface.
func (r *Resource) Update(obj interface{}, req api2go.Request) (api2go.Responder, error) {
	// build context
	ctx := r.buildContext(Update, &req)
	ctx.Model = obj.(Model)
	ctx.Query = bson.M{"_id": ctx.Model.ID()}

	// run authorizer if available
	if err := r.runCallback(r.Authorizer, ctx, http.StatusUnauthorized); err != nil {
		return nil, err
	}

	// validate model
	err := ctx.Model.Validate(false)
	if err != nil {
		return nil, api2go.NewHTTPError(nil, err.Error(), http.StatusBadRequest)
	}

	// run validator if available
	if err := r.runCallback(r.Validator, ctx, http.StatusBadRequest); err != nil {
		return nil, err
	}

	// query db
	err = r.endpoint.db.C(r.Model.Meta().Collection).Update(ctx.Query, ctx.Model)
	if err != nil {
		return nil, api2go.NewHTTPError(err, "Error while updating resource", http.StatusInternalServerError)
	}

	return &api2go.Response{Res: ctx.Model, Code: http.StatusOK}, nil
}

// Delete implements a part of the api2go.CRUD interface.
func (r *Resource) Delete(id string, req api2go.Request) (api2go.Responder, error) {
	// validate id
	if !bson.IsObjectIdHex(id) {
		return nil, api2go.NewHTTPError(nil, "Invalid id", http.StatusBadRequest)
	}

	// build context
	ctx := r.buildContext(Delete, &req)
	ctx.Query = bson.M{"_id": bson.ObjectIdHex(id)}

	// run authorizer if available
	if err := r.runCallback(r.Authorizer, ctx, http.StatusUnauthorized); err != nil {
		return nil, err
	}

	// run validator if available
	if err := r.runCallback(r.Validator, ctx, http.StatusBadRequest); err != nil {
		return nil, err
	}

	// query db
	err := r.endpoint.db.C(r.Model.Meta().Collection).Remove(ctx.Query)
	if err != nil {
		return nil, api2go.NewHTTPError(err, "Error while deleting resource", http.StatusInternalServerError)
	}

	return &api2go.Response{Code: http.StatusNoContent}, nil
}

func (r *Resource) buildContext(act Action, req *api2go.Request) *Context {
	return &Context{
		Action:     act,
		DB:         r.endpoint.db,
		GinContext: r.adapter.getContext(req),
		API2GoReq:  req,
	}
}

func (r *Resource) setRelationshipFilters(ctx *Context) error {
	for param, values := range ctx.API2GoReq.QueryParams {
		// handle *ID params
		if strings.HasSuffix(param, "ID") {
			// get plural name
			pluralName := strings.Replace(param, "ID", "", 1)

			// ret relation name
			relName := ctx.API2GoReq.QueryParams[pluralName+"Name"][0]

			// remove params in any case
			delete(ctx.API2GoReq.QueryParams, param)
			delete(ctx.API2GoReq.QueryParams, pluralName+"Name")

			// get singular name and continue if not existing
			singularName, ok := r.endpoint.nameMap[pluralName]
			if !ok {
				continue
			}

			// check if self referencing
			if singularName == r.Model.Meta().SingularName {
				ctx.Query["_id"] = bson.M{"$in": stringsToIDs(values)}
			}

			for _, field := range r.Model.Meta().Fields {
				// add to one relationship filter
				if field.ToOne && field.RelName == singularName {
					ctx.Query[field.BSONName] = bson.M{"$in": stringsToIDs(values)}
				}

				// add to many relationship filter
				if field.ToMany && field.RelName == pluralName {
					ctx.Query[field.BSONName] = bson.M{"$in": stringsToIDs(values)}
				}

				// add has many relationship filter
				if field.HasMany && field.RelName == pluralName {
					// get referenced resource and continue if not existing
					resource, ok := r.endpoint.resourceMap[singularName]
					if !ok {
						continue
					}

					// prepare key field
					var keyField string

					// get foreign field
					for _, field := range resource.Model.Meta().Fields {
						if field.RelName == relName {
							keyField = field.BSONName
						}
					}

					// check key field
					if keyField == "" {
						return api2go.NewHTTPError(nil, "Error while retrieving key field", http.StatusInternalServerError)
					}

					// read the referenced ids
					var ids []bson.ObjectId
					err := ctx.DB.C(resource.Model.Meta().Collection).Find(bson.M{
						"_id": bson.M{"$in": stringsToIDs(values)},
					}).Distinct(keyField, &ids)
					if err != nil {
						return api2go.NewHTTPError(err, "Error while retrieving resources", http.StatusInternalServerError)
					}

					ctx.Query["_id"] = bson.M{"$in": ids}
				}
			}
		}
	}

	return nil
}

func (r *Resource) runCallback(cb Callback, ctx *Context, errorStatus int) *api2go.HTTPError {
	// check if callback is available
	if cb != nil {
		err := cb(ctx)
		if isFatal(err) {
			// return system error
			httpErr := api2go.NewHTTPError(err, "Internal server error", http.StatusInternalServerError)
			return &httpErr
		}
		if err != nil {
			// return user error
			httpErr := api2go.NewHTTPError(nil, err.Error(), errorStatus)
			return &httpErr
		}
	}

	return nil
}
<commit_msg>added note<commit_after>package fire

import (
	"net/http"
	"reflect"
	"strings"

	"github.com/manyminds/api2go"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

// A Resource provides an interface to a model.
//
// Note: Resources must not be modified after adding to an Endpoint.
type Resource struct {
	// The model that this resource should provide (e.g. &Foo{}).
	Model Model

	// The Authorizer is run on all actions. Will return an Unauthorized status
	// if an user error is returned.
	Authorizer Callback

	// The Validator is run to validate Create, Update and Delete actions. Will
	// return a Bad Request status if an user error is returned.
	Validator Callback

	adapter  *adapter
	endpoint *Endpoint
}

/* api2go interface */

// InitializeObject implements the api2go.ObjectInitializer interface.
func (r *Resource) InitializeObject(obj interface{}) {
	// initialize model
	Init(obj.(Model))
}

// FindAll implements the api2go.FindAll interface.
func (r *Resource) FindAll(req api2go.Request) (api2go.Responder, error) {
	// build context
	ctx := r.buildContext(FindAll, &req)
	ctx.Query = bson.M{}

	// set relationship filters
	err := r.setRelationshipFilters(ctx)
	if err != nil {
		return nil, err
	}

	// add filters
	for _, field := range r.Model.Meta().FieldsByTag("filterable") {
		if values, ok := req.QueryParams["filter["+field.JSONName+"]"]; ok {
			ctx.Query[field.BSONName] = bson.M{"$in": values}
		}
	}

	// add sorting
	if sortParam, ok := req.QueryParams["sort"]; ok {
		for _, params := range sortParam {
			for _, field := range r.Model.Meta().FieldsByTag("sortable") {
				if params == field.BSONName || params == "-"+field.BSONName {
					ctx.Sorting = append(ctx.Sorting, params)
				}
			}
		}
	}

	// run authorizer if available
	if err := r.runCallback(r.Authorizer, ctx, http.StatusUnauthorized); err != nil {
		return nil, err
	}

	// prepare slice
	pointer := r.Model.Meta().MakeSlice()

	// query db
	err = r.endpoint.db.C(r.Model.Meta().Collection).Find(ctx.Query).Sort(ctx.Sorting...).All(pointer)
	if err != nil {
		return nil, api2go.NewHTTPError(err, "Error while retrieving resources", http.StatusInternalServerError)
	}

	// initialize slice
	InitSlice(pointer)

	// api2go needs a direct slice reference
	slice := reflect.ValueOf(pointer).Elem().Interface()

	return &api2go.Response{Res: slice, Code: http.StatusOK}, nil
}

// FindOne implements a part of the api2go.CRUD interface.
func (r *Resource) FindOne(id string, req api2go.Request) (api2go.Responder, error) {
	// validate id
	if !bson.IsObjectIdHex(id) {
		return nil, api2go.NewHTTPError(nil, "Invalid id", http.StatusBadRequest)
	}

	// build context
	ctx := r.buildContext(FindOne, &req)
	ctx.Query = bson.M{"_id": bson.ObjectIdHex(id)}

	// run authorizer if available
	if err := r.runCallback(r.Authorizer, ctx, http.StatusUnauthorized); err != nil {
		return nil, err
	}

	// prepare object
	obj := r.Model.Meta().Make()

	// query db
	err := r.endpoint.db.C(r.Model.Meta().Collection).Find(ctx.Query).One(obj)
	if err == mgo.ErrNotFound {
		return nil, api2go.NewHTTPError(err, "Resource not found", http.StatusNotFound)
	} else if err != nil {
		return nil, api2go.NewHTTPError(err, "Error while retrieving resource", http.StatusInternalServerError)
	}

	// initialize model
	model := Init(obj.(Model))

	return &api2go.Response{Res: model, Code: http.StatusOK}, nil
}

// Create implements a part of the api2go.CRUD interface.
func (r *Resource) Create(obj interface{}, req api2go.Request) (api2go.Responder, error) {
	// build context
	ctx := r.buildContext(Create, &req)
	ctx.Model = obj.(Model)

	// run authorizer if available
	if err := r.runCallback(r.Authorizer, ctx, http.StatusUnauthorized); err != nil {
		return nil, err
	}

	// validate model
	err := ctx.Model.Validate(true)
	if err != nil {
		return nil, api2go.NewHTTPError(nil, err.Error(), http.StatusBadRequest)
	}

	// run validator if available
	if err := r.runCallback(r.Validator, ctx, http.StatusBadRequest); err != nil {
		return nil, err
	}

	// query db
	err = r.endpoint.db.C(r.Model.Meta().Collection).Insert(ctx.Model)
	if err != nil {
		return nil, api2go.NewHTTPError(err, "Error while saving resource", http.StatusInternalServerError)
	}

	return &api2go.Response{Res: ctx.Model, Code: http.StatusCreated}, nil
}

// Update implements a part of the api2go.CRUD interface.
func (r *Resource) Update(obj interface{}, req api2go.Request) (api2go.Responder, error) {
	// build context
	ctx := r.buildContext(Update, &req)
	ctx.Model = obj.(Model)
	ctx.Query = bson.M{"_id": ctx.Model.ID()}

	// run authorizer if available
	if err := r.runCallback(r.Authorizer, ctx, http.StatusUnauthorized); err != nil {
		return nil, err
	}

	// validate model
	err := ctx.Model.Validate(false)
	if err != nil {
		return nil, api2go.NewHTTPError(nil, err.Error(), http.StatusBadRequest)
	}

	// run validator if available
	if err := r.runCallback(r.Validator, ctx, http.StatusBadRequest); err != nil {
		return nil, err
	}

	// query db
	err = r.endpoint.db.C(r.Model.Meta().Collection).Update(ctx.Query, ctx.Model)
	if err != nil {
		return nil, api2go.NewHTTPError(err, "Error while updating resource", http.StatusInternalServerError)
	}

	return &api2go.Response{Res: ctx.Model, Code: http.StatusOK}, nil
}

// Delete implements a part of the api2go.CRUD interface.
func (r *Resource) Delete(id string, req api2go.Request) (api2go.Responder, error) {
	// validate id
	if !bson.IsObjectIdHex(id) {
		return nil, api2go.NewHTTPError(nil, "Invalid id", http.StatusBadRequest)
	}

	// build context
	ctx := r.buildContext(Delete, &req)
	ctx.Query = bson.M{"_id": bson.ObjectIdHex(id)}

	// run authorizer if available
	if err := r.runCallback(r.Authorizer, ctx, http.StatusUnauthorized); err != nil {
		return nil, err
	}

	// run validator if available
	if err := r.runCallback(r.Validator, ctx, http.StatusBadRequest); err != nil {
		return nil, err
	}

	// query db
	err := r.endpoint.db.C(r.Model.Meta().Collection).Remove(ctx.Query)
	if err != nil {
		return nil, api2go.NewHTTPError(err, "Error while deleting resource", http.StatusInternalServerError)
	}

	return &api2go.Response{Code: http.StatusNoContent}, nil
}

func (r *Resource) buildContext(act Action, req *api2go.Request) *Context {
	return &Context{
		Action:     act,
		DB:         r.endpoint.db,
		GinContext: r.adapter.getContext(req),
		API2GoReq:  req,
	}
}

func (r *Resource) setRelationshipFilters(ctx *Context) error {
	// TODO: This is very cumbersome, let's fix it upstream.

	for param, values := range ctx.API2GoReq.QueryParams {
		// handle *ID params
		if strings.HasSuffix(param, "ID") {
			// get plural name
			pluralName := strings.Replace(param, "ID", "", 1)

			// ret relation name
			relName := ctx.API2GoReq.QueryParams[pluralName+"Name"][0]

			// remove params in any case
			delete(ctx.API2GoReq.QueryParams, param)
			delete(ctx.API2GoReq.QueryParams, pluralName+"Name")

			// get singular name and continue if not existing
			singularName, ok := r.endpoint.nameMap[pluralName]
			if !ok {
				continue
			}

			// check if self referencing
			if singularName == r.Model.Meta().SingularName {
				ctx.Query["_id"] = bson.M{"$in": stringsToIDs(values)}
			}

			for _, field := range r.Model.Meta().Fields {
				// add to one relationship filter
				if field.ToOne && field.RelName == singularName {
					ctx.Query[field.BSONName] = bson.M{"$in": stringsToIDs(values)}
				}

				// add to many relationship filter
				if field.ToMany && field.RelName == pluralName {
					ctx.Query[field.BSONName] = bson.M{"$in": stringsToIDs(values)}
				}

				// add has many relationship filter
				if field.HasMany && field.RelName == pluralName {
					// get referenced resource and continue if not existing
					resource, ok := r.endpoint.resourceMap[singularName]
					if !ok {
						continue
					}

					// prepare key field
					var keyField string

					// get foreign field
					for _, field := range resource.Model.Meta().Fields {
						if field.RelName == relName {
							keyField = field.BSONName
						}
					}

					// check key field
					if keyField == "" {
						return api2go.NewHTTPError(nil, "Error while retrieving key field", http.StatusInternalServerError)
					}

					// read the referenced ids
					var ids []bson.ObjectId
					err := ctx.DB.C(resource.Model.Meta().Collection).Find(bson.M{
						"_id": bson.M{"$in": stringsToIDs(values)},
					}).Distinct(keyField, &ids)
					if err != nil {
						return api2go.NewHTTPError(err, "Error while retrieving resources", http.StatusInternalServerError)
					}

					ctx.Query["_id"] = bson.M{"$in": ids}
				}
			}
		}
	}

	return nil
}

func (r *Resource) runCallback(cb Callback, ctx *Context, errorStatus int) *api2go.HTTPError {
	// check if callback is available
	if cb != nil {
		err := cb(ctx)
		if isFatal(err) {
			// return system error
			httpErr := api2go.NewHTTPError(err, "Internal server error", http.StatusInternalServerError)
			return &httpErr
		}
		if err != nil {
			// return user error
			httpErr := api2go.NewHTTPError(nil, err.Error(), errorStatus)
			return &httpErr
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package acomm

import (
	"bytes"
	"encoding/json"
	"errors"
	"net"
	"net/http"
	"net/url"

	log "github.com/Sirupsen/logrus"
	logx "github.com/mistifyio/mistify-logrus-ext"
)

// Response is a response data structure for asynchronous requests. The ID
// should be the same as the Request it corresponds to. Result should be nil if
// Error is present and vice versa.
type Response struct {
	ID     string      `json:"id"`
	Result interface{} `json:"result"`
	Error  error       `json:"error"`
}

// NewResponse creates a new Response instance based on a Request.
func NewResponse(req *Request, result interface{}, err error) (*Response, error) {
	if req == nil {
		err := errors.New("cannot create response without request")
		log.WithFields(log.Fields{
			"errors": err,
		}).Error(err)
		return nil, err
	}

	if result != nil && err != nil {
		err := errors.New("cannot set both result and err")
		log.WithFields(log.Fields{
			"errors": err,
		}).Error(err)
		return nil, err
	}

	if result == nil && err == nil {
		err := errors.New("must set one of either result or err")
		log.WithFields(log.Fields{
			"errors": err,
		}).Error(err)
		return nil, err
	}

	return &Response{
		ID:     req.ID,
		Result: result,
		Error:  err,
	}, nil
}

func (resp *Response) Send(responseHook *url.URL) error {
	switch responseHook.Scheme {
	case "unix":
		return resp.sendUnix(responseHook.String())
	case "http", "https":
		return resp.sendHTTP(responseHook.String())
	default:
		err := errors.New("unknown response hook type")
		log.WithFields(log.Fields{
			"error":        err,
			"type":         responseHook.Scheme,
			"responseHook": responseHook,
		}).Error(err)
		return err
	}
}

// sendUnix sends the Response via a Unix socket.
func (resp *Response) sendUnix(responseHook string) error {
	respJSON, err := json.Marshal(resp)
	if err != nil {
		log.WithFields(log.Fields{
			"error":    err,
			"response": resp,
		}).Error("failed to marshal response json")
		return err
	}

	conn, err := net.Dial("unix", responseHook)
	if err != nil {
		log.WithFields(log.Fields{
			"error":        err,
			"responseHook": responseHook,
			"resp":         resp,
		}).Error("failed to connect to unix socket")
		return err
	}
	defer logx.LogReturnedErr(conn.Close,
		log.Fields{"responseHook": responseHook},
		"failed to close unix connection",
	)

	if _, err := conn.Write(respJSON); err != nil {
		log.WithFields(log.Fields{
			"error":        err,
			"responseHook": responseHook,
			"resp":         resp,
		}).Error("failed to connect to unix socket")
		return err
	}
	return nil
}

// sendHTTP sends the Response via HTTP/HTTPS
func (resp *Response) sendHTTP(responseHook string) error {
	respJSON, err := json.Marshal(resp)
	if err != nil {
		log.WithFields(log.Fields{
			"error":    err,
			"response": resp,
		}).Error("failed to marshal response json")
		return err
	}

	httpResp, err := http.Post(responseHook, "application/json", bytes.NewReader(respJSON))
	if err != nil {
		log.WithFields(log.Fields{
			"error":        err,
			"responseHook": responseHook,
			"resp":         resp,
		}).Error("failed to respond to request")
		return err
	}

	if httpResp.StatusCode != http.StatusOK {
		err := errors.New("unexpected http code for request response")
		log.WithFields(log.Fields{
			"error":        err,
			"responseHook": responseHook,
			"resp":         resp,
			"code":         httpResp.StatusCode,
		}).Error(err)
		return err
	}
	return nil
}
<commit_msg>Use RequestURI for unix address (net.Dial doesn't want the prefix)<commit_after>package acomm

import (
	"bytes"
	"encoding/json"
	"errors"
	"net"
	"net/http"
	"net/url"

	log "github.com/Sirupsen/logrus"
	logx "github.com/mistifyio/mistify-logrus-ext"
)

// Response is a response data structure for asynchronous requests. The ID
// should be the same as the Request it corresponds to. Result should be nil if
// Error is present and vice versa.
type Response struct {
	ID     string      `json:"id"`
	Result interface{} `json:"result"`
	Error  error       `json:"error"`
}

// NewResponse creates a new Response instance based on a Request.
func NewResponse(req *Request, result interface{}, err error) (*Response, error) {
	if req == nil {
		err := errors.New("cannot create response without request")
		log.WithFields(log.Fields{
			"errors": err,
		}).Error(err)
		return nil, err
	}

	if result != nil && err != nil {
		err := errors.New("cannot set both result and err")
		log.WithFields(log.Fields{
			"errors": err,
		}).Error(err)
		return nil, err
	}

	if result == nil && err == nil {
		err := errors.New("must set one of either result or err")
		log.WithFields(log.Fields{
			"errors": err,
		}).Error(err)
		return nil, err
	}

	return &Response{
		ID:     req.ID,
		Result: result,
		Error:  err,
	}, nil
}

func (resp *Response) Send(responseHook *url.URL) error {
	switch responseHook.Scheme {
	case "unix":
		return resp.sendUnix(responseHook.RequestURI())
	case "http", "https":
		return resp.sendHTTP(responseHook.String())
	default:
		err := errors.New("unknown response hook type")
		log.WithFields(log.Fields{
			"error":        err,
			"type":         responseHook.Scheme,
			"responseHook": responseHook,
		}).Error(err)
		return err
	}
}

// sendUnix sends the Response via a Unix socket.
func (resp *Response) sendUnix(responseHook string) error {
	respJSON, err := json.Marshal(resp)
	if err != nil {
		log.WithFields(log.Fields{
			"error":    err,
			"response": resp,
		}).Error("failed to marshal response json")
		return err
	}

	conn, err := net.Dial("unix", responseHook)
	if err != nil {
		log.WithFields(log.Fields{
			"error":        err,
			"responseHook": responseHook,
			"resp":         resp,
		}).Error("failed to connect to unix socket")
		return err
	}
	defer logx.LogReturnedErr(conn.Close,
		log.Fields{"responseHook": responseHook},
		"failed to close unix connection",
	)

	if _, err := conn.Write(respJSON); err != nil {
		log.WithFields(log.Fields{
			"error":        err,
			"responseHook": responseHook,
			"resp":         resp,
		}).Error("failed to connect to unix socket")
		return err
	}
	return nil
}

// sendHTTP sends the Response via HTTP/HTTPS
func (resp *Response) sendHTTP(responseHook string) error {
	respJSON, err := json.Marshal(resp)
	if err != nil {
		log.WithFields(log.Fields{
			"error":    err,
			"response": resp,
		}).Error("failed to marshal response json")
		return err
	}

	httpResp, err := http.Post(responseHook, "application/json", bytes.NewReader(respJSON))
	if err != nil {
		log.WithFields(log.Fields{
			"error":        err,
			"responseHook": responseHook,
			"resp":         resp,
		}).Error("failed to respond to request")
		return err
	}

	if httpResp.StatusCode != http.StatusOK {
		err := errors.New("unexpected http code for request response")
		log.WithFields(log.Fields{
			"error":        err,
			"responseHook": responseHook,
			"resp":         resp,
			"code":         httpResp.StatusCode,
		}).Error(err)
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/binary"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"os"
	"syscall"
)

const (
	GETATTR  = 0x01
	READLINK = 0x02
	GETDIR   = 0x03
	MKNOD    = 0x04
	MKDIR    = 0x05
	SYMLINK  = 0x06
	UNLINK   = 0x07
	RMDIR    = 0x08
	RENAME   = 0x09
	CHMOD    = 0x0A
	CHOWN    = 0x0B
	TRUNCATE = 0x0C
	UTIME    = 0x0D
	OPEN     = 0x0E
	READ     = 0x0F
	WRITE    = 0x10
	READDIR  = 0x11
	RELEASE  = 0x12
	CREATE   = 0x13
)

func handleConn(conn net.Conn) {
	defer conn.Close()
	var err error
	buf := make([]byte, 4096*1024) // 4MB
	openedFile := map[uintptr]*os.File{}
	rootdir := "/home/gle/code_repo/cloud_lib/tcfs-go/rootdir"
	for {
		_, err = io.ReadFull(conn, buf[:4])
		if err != nil {
			break
		}
		msglen := binary.BigEndian.Uint32(buf[:4])
		if msglen < 4 || msglen > (4096*1024) {
			log.Fatal("msglen = ", msglen)
		}
		_, err = io.ReadFull(conn, buf[:msglen])

		tcfsOp := binary.BigEndian.Uint32(buf[0:4])
		msgbuf := buf[4:msglen]
		switch tcfsOp {
		case GETATTR:
			fixpath := rootdir + string(msgbuf)
			var stat syscall.Stat_t
			err = syscall.Lstat(fixpath, &stat)
			if err != nil {
				binary.BigEndian.PutUint32(buf[0:4], 4)
				var ret int32 = -2
				binary.BigEndian.PutUint32(buf[4:8], uint32(ret))
				conn.Write(buf[:8])
				continue
			}
			binary.BigEndian.PutUint32(buf[0:4], 11*4)
			binary.BigEndian.PutUint32(buf[4:8], 0)
			binary.BigEndian.PutUint32(buf[8:12], uint32(stat.Dev))
			binary.BigEndian.PutUint32(buf[12:16], uint32(stat.Ino))
			binary.BigEndian.PutUint32(buf[16:20], stat.Mode)
			binary.BigEndian.PutUint32(buf[20:24], uint32(stat.Nlink))
			binary.BigEndian.PutUint32(buf[24:28], stat.Uid)
			binary.BigEndian.PutUint32(buf[28:32], stat.Gid)
			binary.BigEndian.PutUint32(buf[32:36], uint32(stat.Size))
			binary.BigEndian.PutUint32(buf[36:40], uint32(stat.Atim.Sec))
			binary.BigEndian.PutUint32(buf[40:44], uint32(stat.Mtim.Sec))
			binary.BigEndian.PutUint32(buf[44:48], uint32(stat.Ctim.Sec))
			conn.Write(buf[:48])
		case READLINK:
		case GETDIR:
		case MKNOD:
		case MKDIR:
			mode := binary.BigEndian.Uint32(msgbuf[0:4])
			fixpath := rootdir + string(msgbuf[4:])
			if err := os.MkdirAll(fixpath, os.FileMode(mode)); err != nil {
				log.Print("Can't create dir", err)
				binary.BigEndian.PutUint32(buf[0:4], 4)
				ret := -13
				binary.BigEndian.PutUint32(buf[4:8], uint32(ret))
				conn.Write(buf[:8])
				continue
			}
			binary.BigEndian.PutUint32(buf[0:4], 4)
			binary.BigEndian.PutUint32(buf[4:8], 0)
			conn.Write(buf[:8])
		case SYMLINK:
		case UNLINK:
			// FIXME
			fixpath := rootdir + string(msgbuf)
			// fmt.Println(fixpath)
			if err := os.Remove(fixpath); err != nil {
				log.Print("Can't rmdir", err)
				binary.BigEndian.PutUint32(buf[0:4], 4)
				ret := -13
				binary.BigEndian.PutUint32(buf[4:8], uint32(ret))
				conn.Write(buf[:8])
				continue
			}
			binary.BigEndian.PutUint32(buf[0:4], 4)
			binary.BigEndian.PutUint32(buf[4:8], 0)
			conn.Write(buf[:8])
		case RMDIR:
			fixpath := rootdir + string(msgbuf)
			// fmt.Println(string(msgbuf))
			// fmt.Println(fixpath)
			if err := os.Remove(fixpath); err != nil {
				log.Print("Can't rmdir", err)
				binary.BigEndian.PutUint32(buf[0:4], 4)
				ret := -13
				binary.BigEndian.PutUint32(buf[4:8], uint32(ret))
				conn.Write(buf[:8])
				continue
			}
			binary.BigEndian.PutUint32(buf[0:4], 4)
			binary.BigEndian.PutUint32(buf[4:8], 0)
			conn.Write(buf[:8])
		case RENAME:
		case CHMOD:
			mode := binary.BigEndian.Uint32(msgbuf[0:4])
			fixpath := rootdir + string(msgbuf[4:])
			err := os.Chmod(fixpath, os.FileMode(mode))
			if err != nil {
				log.Print("Can't chmod", err)
				binary.BigEndian.PutUint32(buf[0:4], 4)
				ret := -13
				binary.BigEndian.PutUint32(buf[4:8], uint32(ret))
				conn.Write(buf[:8])
				continue
			}
			binary.BigEndian.PutUint32(buf[0:4], 4)
			binary.BigEndian.PutUint32(buf[4:8], 0)
			conn.Write(buf[:8])
		case CHOWN:
		case TRUNCATE:
			newSize := binary.BigEndian.Uint32(msgbuf[0:4])
			fixpath := rootdir + string(msgbuf[4:])
			err := os.Truncate(fixpath, int64(newSize))
			if err != nil {
				binary.BigEndian.PutUint32(buf[0:4], 4)
				ret := -13 // EACCES
				binary.BigEndian.PutUint32(buf[4:8], uint32(ret))
				conn.Write(buf[:8])
				continue
			}
			binary.BigEndian.PutUint32(buf[0:4], 4)
			binary.BigEndian.PutUint32(buf[4:8], 0)
			conn.Write(buf[:8])
		case UTIME:
			atime := binary.BigEndian.Uint64(msgbuf[0:8])
			mtime := binary.BigEndian.Uint64(msgbuf[8:16])
			fixpath := rootdir + string(msgbuf[16:])
			err := syscall.Utime(fixpath, &syscall.Utimbuf{int64(atime), int64(mtime)})
			if err != nil {
				log.Print("Can't create", err)
				binary.BigEndian.PutUint32(buf[0:4], 4)
				ret := -13
				binary.BigEndian.PutUint32(buf[4:8], uint32(ret))
				conn.Write(buf[:8])
				continue
			}
			binary.BigEndian.PutUint32(buf[0:4], 4)
			binary.BigEndian.PutUint32(buf[4:8], 0)
			conn.Write(buf[:8])
		case OPEN:
			flag := binary.BigEndian.Uint32(msgbuf[0:4])
			fixpath := rootdir + string(msgbuf[4:])
			f, err := os.OpenFile(fixpath, int(flag), os.ModePerm)
			if err != nil {
				binary.BigEndian.PutUint32(buf[0:4], 4)
				var ret int32 = -13
				binary.BigEndian.PutUint32(buf[4:8], uint32(ret))
				conn.Write(buf[:8])
				continue
			}
			fd := f.Fd()
			openedFile[fd] = f
			binary.BigEndian.PutUint32(buf[0:4], 8)
			binary.BigEndian.PutUint32(buf[4:8], 0)
			binary.BigEndian.PutUint32(buf[8:12], uint32(fd))
			conn.Write(buf[:12])
		case READ:
			findex := binary.BigEndian.Uint32(msgbuf[:4])
			offset := binary.BigEndian.Uint32(msgbuf[4:8])
			size := binary.BigEndian.Uint32(msgbuf[8:12])
			f := openedFile[uintptr(findex)]
			readbuf := make([]byte, size)
			readed, err := f.ReadAt(readbuf, int64(offset))
			if err != nil && err != io.EOF {
				binary.BigEndian.PutUint32(buf[0:4], 4)
				var ret int32 = -9
				binary.BigEndian.PutUint32(buf[4:8], uint32(ret))
				conn.Write(buf[:8])
				continue
			}
			if readed == 0 {
				binary.BigEndian.PutUint32(buf[0:4], 4)
				binary.BigEndian.PutUint32(buf[4:8], 0)
				conn.Write(buf[:8])
			} else if readed > 0 {
				binary.BigEndian.PutUint32(buf[0:4], 4+uint32(readed))
				binary.BigEndian.PutUint32(buf[4:8], uint32(readed))
				copy(buf[8:], readbuf)
				conn.Write(buf[:8+readed])
			}
		case WRITE:
			findex := binary.BigEndian.Uint32(msgbuf[:4])
			offset := binary.BigEndian.Uint32(msgbuf[4:8])
			size := binary.BigEndian.Uint32(msgbuf[8:12])
			// fmt.Println(size)
			// fmt.Println(len(msgbuf))
			wbuf := msgbuf[12 : 12+size]
			f := openedFile[uintptr(findex)]
			writed, _ := f.WriteAt(wbuf, int64(offset))
			binary.BigEndian.PutUint32(buf[0:4], 4)
			binary.BigEndian.PutUint32(buf[4:8], uint32(writed))
			conn.Write(buf[:8])
		case READDIR:

			fixpath := rootdir + string(msgbuf)
			fileList := []byte{}
			dirInfo, err := ioutil.ReadDir(fixpath)
			if err != nil {
				log.Print("Can't ReadDir", err)
				continue
			}
			for _, f := range dirInfo {
				fileList = append(fileList, []byte(f.Name())...)
				fileList = append(fileList, 0)
			}

			binary.BigEndian.PutUint32(buf[:4], uint32(len(fileList))+4)
			binary.BigEndian.PutUint32(buf[4:8], 0)
			copy(buf[8:], fileList)
			conn.Write(buf[:len(fileList)+8])
		case RELEASE:
			findex := binary.BigEndian.Uint32(msgbuf[:4])
			f := openedFile[uintptr(findex)]
			err := f.Close()
			if err != nil {
				fmt.Println(err)
				binary.BigEndian.PutUint32(buf[0:4], 4)
				ret := -9
				binary.BigEndian.PutUint32(buf[4:8], uint32(ret))
				conn.Write(buf[:8])
				continue
			}
			binary.BigEndian.PutUint32(buf[0:4], 4)
			binary.BigEndian.PutUint32(buf[4:8], 0)
			conn.Write(buf[:8])
		case CREATE:
			// mode := binary.BigEndian.Uint32([]byte(matched[1])[0:4])
			fixpath := rootdir + string(msgbuf[4:])
			f, err := os.Create(fixpath)
			if err != nil {
				log.Print("Can't create", err)
				binary.BigEndian.PutUint32(buf[0:4], 4)
				ret := -13
				binary.BigEndian.PutUint32(buf[4:8], uint32(ret))
				conn.Write(buf[:8])
				continue
			}
			fd := f.Fd()
			openedFile[fd] = f
			binary.BigEndian.PutUint32(buf[0:4], 8)
			binary.BigEndian.PutUint32(buf[4:8], 0)
			binary.BigEndian.PutUint32(buf[8:12], uint32(fd))
			conn.Write(buf[:12])
		default:
			log.Print("bad tcfsOp: ", tcfsOp)
		}
	}
	// fmt.Println("xxxxxxxxxxx, close")
}

var (
	port = flag.String("port", ":9876", "port to listen to")
)

func main() {
	flag.Parse()
	l, e := net.Listen("tcp", *port)
	if e != nil {
		log.Fatal(e)
	}
	for {
		conn, e := l.Accept()
		if e != nil {
			log.Print(e)
			continue
		}
		go handleConn(conn)
	}
}
<commit_msg>add type TcfsConn<commit_after>package main

import (
	"encoding/binary"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net"
	"os"
	"syscall"
)

type TcfsConn struct {
	RootDir    string
	Conn       net.Conn
	Buf        []byte
	OpenedFile map[uintptr]*os.File
}

const (
	GETATTR  = 0x01
	READLINK = 0x02
	GETDIR   = 0x03
	MKNOD    = 0x04
	MKDIR    = 0x05
	SYMLINK  = 0x06
	UNLINK   = 0x07
	RMDIR    = 0x08
	RENAME   = 0x09
	CHMOD    = 0x0A
	CHOWN    = 0x0B
	TRUNCATE = 0x0C
	UTIME    = 0x0D
	OPEN     = 0x0E
	READ     = 0x0F
	WRITE    = 0x10
	READDIR  = 0x11
	RELEASE  = 0x12
	CREATE   = 0x13
)

func handleConn(tConn *TcfsConn) {
	defer tConn.Conn.Close()
	var err error
	buf := tConn.Buf
	openedFile := tConn.OpenedFile
	rootdir := tConn.RootDir
	conn := tConn.Conn
	for {
		_, err = io.ReadFull(conn, buf[:4])
		if err != nil {
			break
		}
		msglen := binary.BigEndian.Uint32(buf[:4])
		if msglen < 4 || msglen > (4096*1024) {
			log.Fatal("msglen = ", msglen)
		}
		_, err = io.ReadFull(conn, buf[:msglen])

		tcfsOp := binary.BigEndian.Uint32(buf[0:4])
		msgbuf := buf[4:msglen]
		switch tcfsOp {
		case GETATTR:
			fixpath := rootdir + string(msgbuf)
			var stat syscall.Stat_t
			err = syscall.Lstat(fixpath, &stat)
			if err != nil {
				binary.BigEndian.PutUint32(buf[0:4], 4)
				var ret int32 = -2
				binary.BigEndian.PutUint32(buf[4:8], uint32(ret))
				conn.Write(buf[:8])
				continue
			}
			binary.BigEndian.PutUint32(buf[0:4], 11*4)
			binary.BigEndian.PutUint32(buf[4:8], 0)
			binary.BigEndian.PutUint32(buf[8:12], uint32(stat.Dev))
			binary.BigEndian.PutUint32(buf[12:16], uint32(stat.Ino))
			binary.BigEndian.PutUint32(buf[16:20], stat.Mode)
			binary.BigEndian.PutUint32(buf[20:24], uint32(stat.Nlink))
			binary.BigEndian.PutUint32(buf[24:28], stat.Uid)
			binary.BigEndian.PutUint32(buf[28:32], stat.Gid)
			binary.BigEndian.PutUint32(buf[32:36], uint32(stat.Size))
			binary.BigEndian.PutUint32(buf[36:40], uint32(stat.Atim.Sec))
			binary.BigEndian.PutUint32(buf[40:44], uint32(stat.Mtim.Sec))
			binary.BigEndian.PutUint32(buf[44:48], uint32(stat.Ctim.Sec))
			conn.Write(buf[:48])
		case READLINK:
		case GETDIR:
		case MKNOD:
		case MKDIR:
			mode := binary.BigEndian.Uint32(msgbuf[0:4])
			fixpath := rootdir + string(msgbuf[4:])
			if err := os.MkdirAll(fixpath, os.FileMode(mode)); err != nil {
				log.Print("Can't create dir", err)
				binary.BigEndian.PutUint32(buf[0:4], 4)
				ret := -13
				binary.BigEndian.PutUint32(buf[4:8], uint32(ret))
				conn.Write(buf[:8])
				continue
			}
			binary.BigEndian.PutUint32(buf[0:4], 4)
			binary.BigEndian.PutUint32(buf[4:8], 0)
			conn.Write(buf[:8])
		case SYMLINK:
		case UNLINK:
			// FIXME
			fixpath := rootdir + string(msgbuf)
			// fmt.Println(fixpath)
			if err := os.Remove(fixpath); err != nil {
				log.Print("Can't rmdir", err)
				binary.BigEndian.PutUint32(buf[0:4], 4)
				ret := -13
				binary.BigEndian.PutUint32(buf[4:8], uint32(ret))
				conn.Write(buf[:8])
				continue
			}
			binary.BigEndian.PutUint32(buf[0:4], 4)
			binary.BigEndian.PutUint32(buf[4:8], 0)
			conn.Write(buf[:8])
		case RMDIR:
			fixpath := rootdir + string(msgbuf)
			// fmt.Println(string(msgbuf))
			// fmt.Println(fixpath)
			if err := os.Remove(fixpath); err != nil {
				log.Print("Can't rmdir", err)
				binary.BigEndian.PutUint32(buf[0:4], 4)
				ret := -13
				binary.BigEndian.PutUint32(buf[4:8], uint32(ret))
				conn.Write(buf[:8])
				continue
			}
			binary.BigEndian.PutUint32(buf[0:4], 4)
			binary.BigEndian.PutUint32(buf[4:8], 0)
			conn.Write(buf[:8])
		case RENAME:
		case CHMOD:
			mode := binary.BigEndian.Uint32(msgbuf[0:4])
			fixpath := rootdir + string(msgbuf[4:])
			err := os.Chmod(fixpath, os.FileMode(mode))
			if err != nil {
				log.Print("Can't chmod", err)
				binary.BigEndian.PutUint32(buf[0:4], 4)
				ret := -13
				binary.BigEndian.PutUint32(buf[4:8], uint32(ret))
				conn.Write(buf[:8])
				continue
			}
			binary.BigEndian.PutUint32(buf[0:4], 4)
			binary.BigEndian.PutUint32(buf[4:8], 0)
			conn.Write(buf[:8])
		case CHOWN:
		case TRUNCATE:
			newSize := binary.BigEndian.Uint32(msgbuf[0:4])
			fixpath := rootdir + string(msgbuf[4:])
			err := os.Truncate(fixpath, int64(newSize))
			if err != nil {
				binary.BigEndian.PutUint32(buf[0:4], 4)
				ret := -13 // EACCES
				binary.BigEndian.PutUint32(buf[4:8], uint32(ret))
				conn.Write(buf[:8])
				continue
			}
			binary.BigEndian.PutUint32(buf[0:4], 4)
			binary.BigEndian.PutUint32(buf[4:8], 0)
			conn.Write(buf[:8])
		case UTIME:
			atime := binary.BigEndian.Uint64(msgbuf[0:8])
			mtime := binary.BigEndian.Uint64(msgbuf[8:16])
			fixpath := rootdir + string(msgbuf[16:])
			err := syscall.Utime(fixpath, &syscall.Utimbuf{int64(atime), int64(mtime)})
			if err != nil {
				log.Print("Can't create", err)
				binary.BigEndian.PutUint32(buf[0:4], 4)
				ret := -13
				binary.BigEndian.PutUint32(buf[4:8], uint32(ret))
				conn.Write(buf[:8])
				continue
			}
			binary.BigEndian.PutUint32(buf[0:4], 4)
			binary.BigEndian.PutUint32(buf[4:8], 0)
			conn.Write(buf[:8])
		case OPEN:
			flag := binary.BigEndian.Uint32(msgbuf[0:4])
			fixpath := rootdir + string(msgbuf[4:])
			f, err := os.OpenFile(fixpath, int(flag), os.ModePerm)
			if err != nil {
				binary.BigEndian.PutUint32(buf[0:4], 4)
				var ret int32 = -13
				binary.BigEndian.PutUint32(buf[4:8], uint32(ret))
				conn.Write(buf[:8])
				continue
			}
			fd := f.Fd()
			openedFile[fd] = f
			binary.BigEndian.PutUint32(buf[0:4], 8)
			binary.BigEndian.PutUint32(buf[4:8], 0)
			binary.BigEndian.PutUint32(buf[8:12], uint32(fd))
			conn.Write(buf[:12])
		case READ:
			findex := binary.BigEndian.Uint32(msgbuf[:4])
			offset := binary.BigEndian.Uint32(msgbuf[4:8])
			size := binary.BigEndian.Uint32(msgbuf[8:12])
			f := openedFile[uintptr(findex)]
			readbuf := make([]byte, size)
			readed, err := f.ReadAt(readbuf, int64(offset))
			if err != nil && err != io.EOF {
				binary.BigEndian.PutUint32(buf[0:4], 4)
				var ret int32 = -9
				binary.BigEndian.PutUint32(buf[4:8], uint32(ret))
				conn.Write(buf[:8])
				continue
			}
			if readed == 0 {
				binary.BigEndian.PutUint32(buf[0:4], 4)
				binary.BigEndian.PutUint32(buf[4:8], 0)
				conn.Write(buf[:8])
			} else if readed > 0 {
				binary.BigEndian.PutUint32(buf[0:4], 4+uint32(readed))
				binary.BigEndian.PutUint32(buf[4:8], uint32(readed))
				copy(buf[8:], readbuf)
				conn.Write(buf[:8+readed])
			}
		case WRITE:
			findex := binary.BigEndian.Uint32(msgbuf[:4])
			offset := binary.BigEndian.Uint32(msgbuf[4:8])
			size := binary.BigEndian.Uint32(msgbuf[8:12])
			// fmt.Println(size)
			// fmt.Println(len(msgbuf))
			wbuf := msgbuf[12 : 12+size]
			f := openedFile[uintptr(findex)]
			writed, _ := f.WriteAt(wbuf, int64(offset))
			binary.BigEndian.PutUint32(buf[0:4], 4)
			binary.BigEndian.PutUint32(buf[4:8], uint32(writed))
			conn.Write(buf[:8])
		case READDIR:

			fixpath := rootdir + string(msgbuf)
			fileList := []byte{}
			dirInfo, err := ioutil.ReadDir(fixpath)
			if err != nil {
				log.Print("Can't ReadDir", err)
				continue
			}
			for _, f := range dirInfo {
				fileList = append(fileList, []byte(f.Name())...)
				fileList = append(fileList, 0)
			}

			binary.BigEndian.PutUint32(buf[:4], uint32(len(fileList))+4)
			binary.BigEndian.PutUint32(buf[4:8], 0)
			copy(buf[8:], fileList)
			conn.Write(buf[:len(fileList)+8])
		case RELEASE:
			findex := binary.BigEndian.Uint32(msgbuf[:4])
			f := openedFile[uintptr(findex)]
			err := f.Close()
			if err != nil {
				fmt.Println(err)
				binary.BigEndian.PutUint32(buf[0:4], 4)
				ret := -9
				binary.BigEndian.PutUint32(buf[4:8], uint32(ret))
				conn.Write(buf[:8])
				continue
			}
			binary.BigEndian.PutUint32(buf[0:4], 4)
			binary.BigEndian.PutUint32(buf[4:8], 0)
			conn.Write(buf[:8])
		case CREATE:
			// mode := binary.BigEndian.Uint32([]byte(matched[1])[0:4])
			fixpath := rootdir + string(msgbuf[4:])
			f, err := os.Create(fixpath)
			if err != nil {
				log.Print("Can't create", err)
				binary.BigEndian.PutUint32(buf[0:4], 4)
				ret := -13
				binary.BigEndian.PutUint32(buf[4:8], uint32(ret))
				conn.Write(buf[:8])
				continue
			}
			fd := f.Fd()
			openedFile[fd] = f
			binary.BigEndian.PutUint32(buf[0:4], 8)
			binary.BigEndian.PutUint32(buf[4:8], 0)
			binary.BigEndian.PutUint32(buf[8:12], uint32(fd))
			conn.Write(buf[:12])
		default:
			log.Print("bad tcfsOp: ", tcfsOp)
		}
	}
	// fmt.Println("xxxxxxxxxxx, close")
}

var (
	port = flag.String("port", ":9876", "port to listen to")
)

func main() {
	flag.Parse()
	l, e := net.Listen("tcp", *port)
	if e != nil {
		log.Fatal(e)
	}
	rootpath := "/home/gle/code_repo/cloud_lib/tcfs-go/rootdir"
	for {
		conn, e := l.Accept()
		if e != nil {
			log.Print(e)
			continue
		}
		newConn := TcfsConn{rootpath, conn,
			make([]byte, 4096*1024),
			map[uintptr]*os.File{}}
		go handleConn(&newConn)
	}
}
<|endoftext|>


<commit_before>// The nilness command applies the golang.org/x/tools/go/analysis/passes/lostcancel
// analysis to the specified packages of Go source code.
package main

import (
	"golang.org/x/tools/go/analysis/passes/lostcancel"
	"golang.org/x/tools/go/analysis/singlechecker"
)

func main() { singlechecker.Main(lostcancel.Analyzer) }
<commit_msg>go/analysis: fix package doc copy-paste typo<commit_after>// The lostcancel command applies the golang.org/x/tools/go/analysis/passes/lostcancel
// analysis to the specified packages of Go source code.
package main

import (
	"golang.org/x/tools/go/analysis/passes/lostcancel"
	"golang.org/x/tools/go/analysis/singlechecker"
)

func main() { singlechecker.Main(lostcancel.Analyzer) }
<|endoftext|>