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

package attest

import (
	"bytes"
	"crypto"
	"encoding/asn1"
	"encoding/binary"
	"fmt"
	"io"
	"math/big"
	"strings"

	"github.com/google/certificate-transparency-go/x509"

	"github.com/google/go-tpm/tpm2"
	"github.com/google/go-tpm/tpmutil"
)

const (
	tpmPtManufacturer = 0x00000100 + 5 // PT_FIXED + offset of 5
	tpmPtVendorString = 0x00000100 + 6 // PT_FIXED + offset of 6

	// Defined in "Registry of reserved TPM 2.0 handles and localities".
	nvramCertIndex = 0x1c00002

	// Defined in "Registry of reserved TPM 2.0 handles and localities", and checked on a glinux machine.
	commonSrkEquivalentHandle = 0x81000001
	commonEkEquivalentHandle  = 0x81010001
)

var (
	aikTemplate = tpm2.Public{
		Type:       tpm2.AlgRSA,
		NameAlg:    tpm2.AlgSHA256,
		Attributes: tpm2.FlagSignerDefault | tpm2.FlagNoDA,
		RSAParameters: &tpm2.RSAParams{
			Sign: &tpm2.SigScheme{
				Alg:  tpm2.AlgRSASSA,
				Hash: tpm2.AlgSHA256,
			},
			KeyBits: 2048,
			Modulus: big.NewInt(0),
		},
	}
	defaultSRKTemplate = tpm2.Public{
		Type:       tpm2.AlgRSA,
		NameAlg:    tpm2.AlgSHA256,
		Attributes: tpm2.FlagStorageDefault,
		RSAParameters: &tpm2.RSAParams{
			Symmetric: &tpm2.SymScheme{
				Alg:     tpm2.AlgAES,
				KeyBits: 128,
				Mode:    tpm2.AlgCFB,
			},
			KeyBits: 2048,
			Modulus: big.NewInt(0),
		},
	}
	// Default EK template defined in:
	// https://trustedcomputinggroup.org/wp-content/uploads/Credential_Profile_EK_V2.0_R14_published.pdf
	defaultEKTemplate = tpm2.Public{
		Type:    tpm2.AlgRSA,
		NameAlg: tpm2.AlgSHA256,
		Attributes: tpm2.FlagFixedTPM | tpm2.FlagFixedParent | tpm2.FlagSensitiveDataOrigin |
			tpm2.FlagAdminWithPolicy | tpm2.FlagRestricted | tpm2.FlagDecrypt,
		AuthPolicy: []byte{
			0x83, 0x71, 0x97, 0x67, 0x44, 0x84,
			0xB3, 0xF8, 0x1A, 0x90, 0xCC, 0x8D,
			0x46, 0xA5, 0xD7, 0x24, 0xFD, 0x52,
			0xD7, 0x6E, 0x06, 0x52, 0x0B, 0x64,
			0xF2, 0xA1, 0xDA, 0x1B, 0x33, 0x14,
			0x69, 0xAA,
		},
		RSAParameters: &tpm2.RSAParams{
			Symmetric: &tpm2.SymScheme{
				Alg:     tpm2.AlgAES,
				KeyBits: 128,
				Mode:    tpm2.AlgCFB,
			},
			KeyBits:    2048,
			Exponent:   0,
			ModulusRaw: make([]byte, 256),
		},
	}
)

func readTPM2VendorAttributes(tpm io.ReadWriter) (TCGVendorID, string, error) {
	var vendorInfo string
	// The Vendor String is split up into 4 sections of 4 bytes,
	// for a maximum length of 16 octets of ASCII text. We iterate
	// through the 4 indexes to get all 16 bytes & construct vendorInfo.
	// See: TPM_PT_VENDOR_STRING_1 in TPM 2.0 Structures reference.
	for i := 0; i < 4; i++ {
		caps, _, err := tpm2.GetCapability(tpm, tpm2.CapabilityTPMProperties, 1, tpmPtVendorString+uint32(i))
		if err != nil {
			return TCGVendorID(0), "", fmt.Errorf("tpm2.GetCapability(PT_VENDOR_STRING_%d) failed: %v", i+1, err)
		}
		subset, ok := caps[0].(tpm2.TaggedProperty)
		if !ok {
			return TCGVendorID(0), "", fmt.Errorf("got capability of type %T, want tpm2.TaggedProperty", caps[0])
		}
		// Reconstruct the 4 ASCII octets from the uint32 value.
		vendorInfo += string(subset.Value&0xFF000000) + string(subset.Value&0xFF0000) + string(subset.Value&0xFF00) + string(subset.Value&0xFF)
	}

	caps, _, err := tpm2.GetCapability(tpm, tpm2.CapabilityTPMProperties, 1, tpmPtManufacturer)
	if err != nil {
		return TCGVendorID(0), "", fmt.Errorf("tpm2.GetCapability(PT_MANUFACTURER) failed: %v", err)
	}
	manu, ok := caps[0].(tpm2.TaggedProperty)
	if !ok {
		return TCGVendorID(0), "", fmt.Errorf("got capability of type %T, want tpm2.TaggedProperty", caps[0])
	}

	return TCGVendorID(manu.Value), strings.Trim(vendorInfo, "\x00"), nil
}

func parseCert(ekCert []byte) (*x509.Certificate, error) {
	// TCG PC Specific Implementation section 7.3.2 specifies
	// a prefix when storing a certificate in NVRAM. We look
	// for and unwrap the certificate if its present.
	if len(ekCert) > 5 && bytes.Equal(ekCert[:3], []byte{0x10, 0x01, 0x00}) {
		certLen := binary.BigEndian.Uint16(ekCert[3:5])
		if len(ekCert) < int(certLen+5) {
			return nil, fmt.Errorf("parsing nvram header: ekCert size %d smaller than specified cert length %d", len(ekCert), certLen)
		}
		ekCert = ekCert[5 : 5+certLen]
	}

	// If the cert parses fine without any changes, we are G2G.
	if c, err := x509.ParseCertificate(ekCert); err == nil {
		return c, nil
	}
	// There might be trailing nonsense in the cert, which Go
	// does not parse correctly. As ASN1 data is TLV encoded, we should
	// be able to just get the certificate, and then send that to Go's
	// certificate parser.
	var cert struct {
		Raw asn1.RawContent
	}
	if _, err := asn1.Unmarshal(ekCert, &cert); err != nil {
		return nil, err
	}
	c, err := x509.ParseCertificate(cert.Raw)
	if err != nil && x509.IsFatal(err) {
		return nil, err
	}
	return c, nil
}

func readEKCertFromNVRAM20(tpm io.ReadWriter) (*x509.Certificate, error) {
	ekCert, err := tpm2.NVReadEx(tpm, nvramCertIndex, tpm2.HandleOwner, "", 0)
	if err != nil {
		return nil, fmt.Errorf("reading EK cert: %v", err)
	}
	return parseCert(ekCert)
}

func quote20(tpm io.ReadWriter, aikHandle tpmutil.Handle, hashAlg tpm2.Algorithm, nonce []byte) (*Quote, error) {
	sel := tpm2.PCRSelection{Hash: hashAlg}
	numPCRs := 24
	for pcr := 0; pcr < numPCRs; pcr++ {
		sel.PCRs = append(sel.PCRs, pcr)
	}

	quote, sig, err := tpm2.Quote(tpm, aikHandle, "", "", nonce, sel, tpm2.AlgNull)
	if err != nil {
		return nil, err
	}

	rawSig, err := tpmutil.Pack(sig.Alg, sig.RSA.HashAlg, sig.RSA.Signature)
	return &Quote{
		Version:   TPMVersion20,
		Quote:     quote,
		Signature: rawSig,
	}, err
}

func readAllPCRs20(tpm io.ReadWriter, alg tpm2.Algorithm) (map[uint32][]byte, error) {
	numPCRs := 24
	out := map[uint32][]byte{}

	// The TPM 2.0 spec says that the TPM can partially fulfill the
	// request. As such, we repeat the command up to 8 times to get all
	// 24 PCRs.
	for i := 0; i < numPCRs; i++ {
		// Build a selection structure, specifying all PCRs we do
		// not have the value for.
		sel := tpm2.PCRSelection{Hash: alg}
		for pcr := 0; pcr < numPCRs; pcr++ {
			if _, present := out[uint32(pcr)]; !present {
				sel.PCRs = append(sel.PCRs, pcr)
			}
		}

		// Ask the TPM for those PCR values.
		ret, err := tpm2.ReadPCRs(tpm, sel)
		if err != nil {
			return nil, fmt.Errorf("tpm2.ReadPCRs(%+v) failed with err: %v", sel, err)
		}
		// Keep track of the PCRs we were actually given.
		for pcr, digest := range ret {
			out[uint32(pcr)] = digest
		}
		if len(out) == numPCRs {
			break
		}
	}

	if len(out) != numPCRs {
		return nil, fmt.Errorf("failed to read all PCRs, only read %d", len(out))
	}

	return out, nil
}

func allPCRs20(tpm io.ReadWriter) (map[uint32][]byte, crypto.Hash, error) {
	out256, err256 := readAllPCRs20(tpm, tpm2.AlgSHA256)
	if err256 != nil {
		// TPM may not implement active banks with SHA256 - try SHA1.
		out1, err1 := readAllPCRs20(tpm, tpm2.AlgSHA1)
		return out1, crypto.SHA1, err1
	}
	return out256, crypto.SHA256, nil
}
<commit_msg>Better error messages for parseCert() (#39)<commit_after>// Copyright 2019 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

package attest

import (
	"bytes"
	"crypto"
	"encoding/asn1"
	"encoding/binary"
	"fmt"
	"io"
	"math/big"
	"strings"

	"github.com/google/certificate-transparency-go/x509"

	"github.com/google/go-tpm/tpm2"
	"github.com/google/go-tpm/tpmutil"
)

const (
	tpmPtManufacturer = 0x00000100 + 5 // PT_FIXED + offset of 5
	tpmPtVendorString = 0x00000100 + 6 // PT_FIXED + offset of 6

	// Defined in "Registry of reserved TPM 2.0 handles and localities".
	nvramCertIndex = 0x1c00002

	// Defined in "Registry of reserved TPM 2.0 handles and localities", and checked on a glinux machine.
	commonSrkEquivalentHandle = 0x81000001
	commonEkEquivalentHandle  = 0x81010001
)

var (
	aikTemplate = tpm2.Public{
		Type:       tpm2.AlgRSA,
		NameAlg:    tpm2.AlgSHA256,
		Attributes: tpm2.FlagSignerDefault | tpm2.FlagNoDA,
		RSAParameters: &tpm2.RSAParams{
			Sign: &tpm2.SigScheme{
				Alg:  tpm2.AlgRSASSA,
				Hash: tpm2.AlgSHA256,
			},
			KeyBits: 2048,
			Modulus: big.NewInt(0),
		},
	}
	defaultSRKTemplate = tpm2.Public{
		Type:       tpm2.AlgRSA,
		NameAlg:    tpm2.AlgSHA256,
		Attributes: tpm2.FlagStorageDefault,
		RSAParameters: &tpm2.RSAParams{
			Symmetric: &tpm2.SymScheme{
				Alg:     tpm2.AlgAES,
				KeyBits: 128,
				Mode:    tpm2.AlgCFB,
			},
			KeyBits: 2048,
			Modulus: big.NewInt(0),
		},
	}
	// Default EK template defined in:
	// https://trustedcomputinggroup.org/wp-content/uploads/Credential_Profile_EK_V2.0_R14_published.pdf
	defaultEKTemplate = tpm2.Public{
		Type:    tpm2.AlgRSA,
		NameAlg: tpm2.AlgSHA256,
		Attributes: tpm2.FlagFixedTPM | tpm2.FlagFixedParent | tpm2.FlagSensitiveDataOrigin |
			tpm2.FlagAdminWithPolicy | tpm2.FlagRestricted | tpm2.FlagDecrypt,
		AuthPolicy: []byte{
			0x83, 0x71, 0x97, 0x67, 0x44, 0x84,
			0xB3, 0xF8, 0x1A, 0x90, 0xCC, 0x8D,
			0x46, 0xA5, 0xD7, 0x24, 0xFD, 0x52,
			0xD7, 0x6E, 0x06, 0x52, 0x0B, 0x64,
			0xF2, 0xA1, 0xDA, 0x1B, 0x33, 0x14,
			0x69, 0xAA,
		},
		RSAParameters: &tpm2.RSAParams{
			Symmetric: &tpm2.SymScheme{
				Alg:     tpm2.AlgAES,
				KeyBits: 128,
				Mode:    tpm2.AlgCFB,
			},
			KeyBits:    2048,
			Exponent:   0,
			ModulusRaw: make([]byte, 256),
		},
	}
)

func readTPM2VendorAttributes(tpm io.ReadWriter) (TCGVendorID, string, error) {
	var vendorInfo string
	// The Vendor String is split up into 4 sections of 4 bytes,
	// for a maximum length of 16 octets of ASCII text. We iterate
	// through the 4 indexes to get all 16 bytes & construct vendorInfo.
	// See: TPM_PT_VENDOR_STRING_1 in TPM 2.0 Structures reference.
	for i := 0; i < 4; i++ {
		caps, _, err := tpm2.GetCapability(tpm, tpm2.CapabilityTPMProperties, 1, tpmPtVendorString+uint32(i))
		if err != nil {
			return TCGVendorID(0), "", fmt.Errorf("tpm2.GetCapability(PT_VENDOR_STRING_%d) failed: %v", i+1, err)
		}
		subset, ok := caps[0].(tpm2.TaggedProperty)
		if !ok {
			return TCGVendorID(0), "", fmt.Errorf("got capability of type %T, want tpm2.TaggedProperty", caps[0])
		}
		// Reconstruct the 4 ASCII octets from the uint32 value.
		vendorInfo += string(subset.Value&0xFF000000) + string(subset.Value&0xFF0000) + string(subset.Value&0xFF00) + string(subset.Value&0xFF)
	}

	caps, _, err := tpm2.GetCapability(tpm, tpm2.CapabilityTPMProperties, 1, tpmPtManufacturer)
	if err != nil {
		return TCGVendorID(0), "", fmt.Errorf("tpm2.GetCapability(PT_MANUFACTURER) failed: %v", err)
	}
	manu, ok := caps[0].(tpm2.TaggedProperty)
	if !ok {
		return TCGVendorID(0), "", fmt.Errorf("got capability of type %T, want tpm2.TaggedProperty", caps[0])
	}

	return TCGVendorID(manu.Value), strings.Trim(vendorInfo, "\x00"), nil
}

func parseCert(ekCert []byte) (*x509.Certificate, error) {
	// TCG PC Specific Implementation section 7.3.2 specifies
	// a prefix when storing a certificate in NVRAM. We look
	// for and unwrap the certificate if its present.
	if len(ekCert) > 5 && bytes.Equal(ekCert[:3], []byte{0x10, 0x01, 0x00}) {
		certLen := binary.BigEndian.Uint16(ekCert[3:5])
		if len(ekCert) < int(certLen+5) {
			return nil, fmt.Errorf("parsing nvram header: ekCert size %d smaller than specified cert length %d", len(ekCert), certLen)
		}
		ekCert = ekCert[5 : 5+certLen]
	}

	// If the cert parses fine without any changes, we are G2G.
	if c, err := x509.ParseCertificate(ekCert); err == nil {
		return c, nil
	}
	// There might be trailing nonsense in the cert, which Go
	// does not parse correctly. As ASN1 data is TLV encoded, we should
	// be able to just get the certificate, and then send that to Go's
	// certificate parser.
	var cert struct {
		Raw asn1.RawContent
	}
	if _, err := asn1.Unmarshal(ekCert, &cert); err != nil {
		return nil, fmt.Errorf("asn1.Unmarshal() failed: %v", err)
	}
	c, err := x509.ParseCertificate(cert.Raw)
	if err != nil && x509.IsFatal(err) {
		return nil, fmt.Errorf("x509.ParseCertificate() failed: %v", err)
	}
	return c, nil
}

func readEKCertFromNVRAM20(tpm io.ReadWriter) (*x509.Certificate, error) {
	ekCert, err := tpm2.NVReadEx(tpm, nvramCertIndex, tpm2.HandleOwner, "", 0)
	if err != nil {
		return nil, fmt.Errorf("reading EK cert: %v", err)
	}
	return parseCert(ekCert)
}

func quote20(tpm io.ReadWriter, aikHandle tpmutil.Handle, hashAlg tpm2.Algorithm, nonce []byte) (*Quote, error) {
	sel := tpm2.PCRSelection{Hash: hashAlg}
	numPCRs := 24
	for pcr := 0; pcr < numPCRs; pcr++ {
		sel.PCRs = append(sel.PCRs, pcr)
	}

	quote, sig, err := tpm2.Quote(tpm, aikHandle, "", "", nonce, sel, tpm2.AlgNull)
	if err != nil {
		return nil, err
	}

	rawSig, err := tpmutil.Pack(sig.Alg, sig.RSA.HashAlg, sig.RSA.Signature)
	return &Quote{
		Version:   TPMVersion20,
		Quote:     quote,
		Signature: rawSig,
	}, err
}

func readAllPCRs20(tpm io.ReadWriter, alg tpm2.Algorithm) (map[uint32][]byte, error) {
	numPCRs := 24
	out := map[uint32][]byte{}

	// The TPM 2.0 spec says that the TPM can partially fulfill the
	// request. As such, we repeat the command up to 8 times to get all
	// 24 PCRs.
	for i := 0; i < numPCRs; i++ {
		// Build a selection structure, specifying all PCRs we do
		// not have the value for.
		sel := tpm2.PCRSelection{Hash: alg}
		for pcr := 0; pcr < numPCRs; pcr++ {
			if _, present := out[uint32(pcr)]; !present {
				sel.PCRs = append(sel.PCRs, pcr)
			}
		}

		// Ask the TPM for those PCR values.
		ret, err := tpm2.ReadPCRs(tpm, sel)
		if err != nil {
			return nil, fmt.Errorf("tpm2.ReadPCRs(%+v) failed with err: %v", sel, err)
		}
		// Keep track of the PCRs we were actually given.
		for pcr, digest := range ret {
			out[uint32(pcr)] = digest
		}
		if len(out) == numPCRs {
			break
		}
	}

	if len(out) != numPCRs {
		return nil, fmt.Errorf("failed to read all PCRs, only read %d", len(out))
	}

	return out, nil
}

func allPCRs20(tpm io.ReadWriter) (map[uint32][]byte, crypto.Hash, error) {
	out256, err256 := readAllPCRs20(tpm, tpm2.AlgSHA256)
	if err256 != nil {
		// TPM may not implement active banks with SHA256 - try SHA1.
		out1, err1 := readAllPCRs20(tpm, tpm2.AlgSHA1)
		return out1, crypto.SHA1, err1
	}
	return out256, crypto.SHA256, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package common

import (
	"fmt"

	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/uuid"
	utilversion "k8s.io/kubernetes/pkg/util/version"
	"k8s.io/kubernetes/test/e2e/framework"

	. "github.com/onsi/ginkgo"
)

var hostIPVersion = utilversion.MustParseSemantic("v1.8.0")

var _ = framework.KubeDescribe("Downward API", func() {
	f := framework.NewDefaultFramework("downward-api")

	It("should provide pod name and namespace as env vars [Conformance]", func() {
		podName := "downward-api-" + string(uuid.NewUUID())
		env := []v1.EnvVar{
			{
				Name: "POD_NAME",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						APIVersion: "v1",
						FieldPath:  "metadata.name",
					},
				},
			},
			{
				Name: "POD_NAMESPACE",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						APIVersion: "v1",
						FieldPath:  "metadata.namespace",
					},
				},
			},
		}

		expectations := []string{
			fmt.Sprintf("POD_NAME=%v", podName),
			fmt.Sprintf("POD_NAMESPACE=%v", f.Namespace.Name),
		}

		testDownwardAPI(f, podName, env, expectations)
	})

	It("should provide pod and host IP as an env var [Conformance]", func() {
		framework.SkipUnlessServerVersionGTE(hostIPVersion, f.ClientSet.Discovery())
		podName := "downward-api-" + string(uuid.NewUUID())
		env := []v1.EnvVar{
			{
				Name: "POD_IP",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						APIVersion: "v1",
						FieldPath:  "status.podIP",
					},
				},
			},
			{
				Name: "HOST_IP",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						APIVersion: "v1",
						FieldPath:  "status.hostIP",
					},
				},
			},
		}

		expectations := []string{
			"POD_IP=(?:\\d+)\\.(?:\\d+)\\.(?:\\d+)\\.(?:\\d+)",
			"HOST_IP=(?:\\d+)\\.(?:\\d+)\\.(?:\\d+)\\.(?:\\d+)",
		}

		testDownwardAPI(f, podName, env, expectations)
	})

	It("should provide container's limits.cpu/memory and requests.cpu/memory as env vars [Conformance]", func() {
		podName := "downward-api-" + string(uuid.NewUUID())
		env := []v1.EnvVar{
			{
				Name: "CPU_LIMIT",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "limits.cpu",
					},
				},
			},
			{
				Name: "MEMORY_LIMIT",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "limits.memory",
					},
				},
			},
			{
				Name: "CPU_REQUEST",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "requests.cpu",
					},
				},
			},
			{
				Name: "MEMORY_REQUEST",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "requests.memory",
					},
				},
			},
		}
		expectations := []string{
			fmt.Sprintf("CPU_LIMIT=2"),
			fmt.Sprintf("MEMORY_LIMIT=67108864"),
			fmt.Sprintf("CPU_REQUEST=1"),
			fmt.Sprintf("MEMORY_REQUEST=33554432"),
		}

		testDownwardAPI(f, podName, env, expectations)
	})

	It("should provide default limits.cpu/memory from node allocatable [Conformance]", func() {
		podName := "downward-api-" + string(uuid.NewUUID())
		env := []v1.EnvVar{
			{
				Name: "CPU_LIMIT",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "limits.cpu",
					},
				},
			},
			{
				Name: "MEMORY_LIMIT",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "limits.memory",
					},
				},
			},
		}
		expectations := []string{
			fmt.Sprintf("CPU_LIMIT=[1-9]"),
			fmt.Sprintf("MEMORY_LIMIT=[1-9]"),
		}
		pod := &v1.Pod{
			ObjectMeta: metav1.ObjectMeta{
				Name:   podName,
				Labels: map[string]string{"name": podName},
			},
			Spec: v1.PodSpec{
				Containers: []v1.Container{
					{
						Name:    "dapi-container",
						Image:   busyboxImage,
						Command: []string{"sh", "-c", "env"},
						Env:     env,
					},
				},
				RestartPolicy: v1.RestartPolicyNever,
			},
		}

		testDownwardAPIUsingPod(f, pod, env, expectations)
	})

	It("should provide pod UID as env vars [Conformance]", func() {
		framework.SkipUnlessServerVersionGTE(hostIPVersion, f.ClientSet.Discovery())
		podName := "downward-api-" + string(uuid.NewUUID())
		env := []v1.EnvVar{
			{
				Name: "POD_UID",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						APIVersion: "v1",
						FieldPath:  "metadata.uid",
					},
				},
			},
		}

		expectations := []string{
			fmt.Sprintf("POD_UID=[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}"),
		}

		testDownwardAPI(f, podName, env, expectations)
	})
})

func testDownwardAPI(f *framework.Framework, podName string, env []v1.EnvVar, expectations []string) {
	pod := &v1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			Name:   podName,
			Labels: map[string]string{"name": podName},
		},
		Spec: v1.PodSpec{
			Containers: []v1.Container{
				{
					Name:    "dapi-container",
					Image:   busyboxImage,
					Command: []string{"sh", "-c", "env"},
					Resources: v1.ResourceRequirements{
						Requests: v1.ResourceList{
							v1.ResourceCPU:    resource.MustParse("250m"),
							v1.ResourceMemory: resource.MustParse("32Mi"),
						},
						Limits: v1.ResourceList{
							v1.ResourceCPU:    resource.MustParse("1250m"),
							v1.ResourceMemory: resource.MustParse("64Mi"),
						},
					},
					Env: env,
				},
			},
			RestartPolicy: v1.RestartPolicyNever,
		},
	}

	testDownwardAPIUsingPod(f, pod, env, expectations)
}

func testDownwardAPIUsingPod(f *framework.Framework, pod *v1.Pod, env []v1.EnvVar, expectations []string) {
	f.TestContainerOutputRegexp("downward api env vars", pod, 0, expectations)
}
<commit_msg>Define pod UID version.<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package common

import (
	"fmt"

	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/uuid"
	utilversion "k8s.io/kubernetes/pkg/util/version"
	"k8s.io/kubernetes/test/e2e/framework"

	. "github.com/onsi/ginkgo"
)

var (
	hostIPVersion = utilversion.MustParseSemantic("v1.8.0")
	podUIDVersion = utilversion.MustParseSemantic("v1.8.0")
)

var _ = framework.KubeDescribe("Downward API", func() {
	f := framework.NewDefaultFramework("downward-api")

	It("should provide pod name and namespace as env vars [Conformance]", func() {
		podName := "downward-api-" + string(uuid.NewUUID())
		env := []v1.EnvVar{
			{
				Name: "POD_NAME",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						APIVersion: "v1",
						FieldPath:  "metadata.name",
					},
				},
			},
			{
				Name: "POD_NAMESPACE",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						APIVersion: "v1",
						FieldPath:  "metadata.namespace",
					},
				},
			},
		}

		expectations := []string{
			fmt.Sprintf("POD_NAME=%v", podName),
			fmt.Sprintf("POD_NAMESPACE=%v", f.Namespace.Name),
		}

		testDownwardAPI(f, podName, env, expectations)
	})

	It("should provide pod and host IP as an env var [Conformance]", func() {
		framework.SkipUnlessServerVersionGTE(hostIPVersion, f.ClientSet.Discovery())
		podName := "downward-api-" + string(uuid.NewUUID())
		env := []v1.EnvVar{
			{
				Name: "POD_IP",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						APIVersion: "v1",
						FieldPath:  "status.podIP",
					},
				},
			},
			{
				Name: "HOST_IP",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						APIVersion: "v1",
						FieldPath:  "status.hostIP",
					},
				},
			},
		}

		expectations := []string{
			"POD_IP=(?:\\d+)\\.(?:\\d+)\\.(?:\\d+)\\.(?:\\d+)",
			"HOST_IP=(?:\\d+)\\.(?:\\d+)\\.(?:\\d+)\\.(?:\\d+)",
		}

		testDownwardAPI(f, podName, env, expectations)
	})

	It("should provide container's limits.cpu/memory and requests.cpu/memory as env vars [Conformance]", func() {
		podName := "downward-api-" + string(uuid.NewUUID())
		env := []v1.EnvVar{
			{
				Name: "CPU_LIMIT",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "limits.cpu",
					},
				},
			},
			{
				Name: "MEMORY_LIMIT",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "limits.memory",
					},
				},
			},
			{
				Name: "CPU_REQUEST",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "requests.cpu",
					},
				},
			},
			{
				Name: "MEMORY_REQUEST",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "requests.memory",
					},
				},
			},
		}
		expectations := []string{
			fmt.Sprintf("CPU_LIMIT=2"),
			fmt.Sprintf("MEMORY_LIMIT=67108864"),
			fmt.Sprintf("CPU_REQUEST=1"),
			fmt.Sprintf("MEMORY_REQUEST=33554432"),
		}

		testDownwardAPI(f, podName, env, expectations)
	})

	It("should provide default limits.cpu/memory from node allocatable [Conformance]", func() {
		podName := "downward-api-" + string(uuid.NewUUID())
		env := []v1.EnvVar{
			{
				Name: "CPU_LIMIT",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "limits.cpu",
					},
				},
			},
			{
				Name: "MEMORY_LIMIT",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "limits.memory",
					},
				},
			},
		}
		expectations := []string{
			fmt.Sprintf("CPU_LIMIT=[1-9]"),
			fmt.Sprintf("MEMORY_LIMIT=[1-9]"),
		}
		pod := &v1.Pod{
			ObjectMeta: metav1.ObjectMeta{
				Name:   podName,
				Labels: map[string]string{"name": podName},
			},
			Spec: v1.PodSpec{
				Containers: []v1.Container{
					{
						Name:    "dapi-container",
						Image:   busyboxImage,
						Command: []string{"sh", "-c", "env"},
						Env:     env,
					},
				},
				RestartPolicy: v1.RestartPolicyNever,
			},
		}

		testDownwardAPIUsingPod(f, pod, env, expectations)
	})

	It("should provide pod UID as env vars [Conformance]", func() {
		framework.SkipUnlessServerVersionGTE(podUIDVersion, f.ClientSet.Discovery())
		podName := "downward-api-" + string(uuid.NewUUID())
		env := []v1.EnvVar{
			{
				Name: "POD_UID",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						APIVersion: "v1",
						FieldPath:  "metadata.uid",
					},
				},
			},
		}

		expectations := []string{
			fmt.Sprintf("POD_UID=[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}"),
		}

		testDownwardAPI(f, podName, env, expectations)
	})
})

func testDownwardAPI(f *framework.Framework, podName string, env []v1.EnvVar, expectations []string) {
	pod := &v1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			Name:   podName,
			Labels: map[string]string{"name": podName},
		},
		Spec: v1.PodSpec{
			Containers: []v1.Container{
				{
					Name:    "dapi-container",
					Image:   busyboxImage,
					Command: []string{"sh", "-c", "env"},
					Resources: v1.ResourceRequirements{
						Requests: v1.ResourceList{
							v1.ResourceCPU:    resource.MustParse("250m"),
							v1.ResourceMemory: resource.MustParse("32Mi"),
						},
						Limits: v1.ResourceList{
							v1.ResourceCPU:    resource.MustParse("1250m"),
							v1.ResourceMemory: resource.MustParse("64Mi"),
						},
					},
					Env: env,
				},
			},
			RestartPolicy: v1.RestartPolicyNever,
		},
	}

	testDownwardAPIUsingPod(f, pod, env, expectations)
}

func testDownwardAPIUsingPod(f *framework.Framework, pod *v1.Pod, env []v1.EnvVar, expectations []string) {
	f.TestContainerOutputRegexp("downward api env vars", pod, 0, expectations)
}
<|endoftext|>


<commit_before>/*
   Copyright The containerd Authors.

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

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

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

package runc

import (
	"context"
	"io"
	"sync"
	"syscall"

	"github.com/containerd/console"
	"github.com/containerd/fifo"
	"github.com/pkg/errors"
)

type linuxPlatform struct {
	epoller *console.Epoller
}

func (p *linuxPlatform) CopyConsole(ctx context.Context, console console.Console, stdin, stdout, stderr string, wg, cwg *sync.WaitGroup) (console.Console, error) {
	if p.epoller == nil {
		return nil, errors.New("uninitialized epoller")
	}

	epollConsole, err := p.epoller.Add(console)
	if err != nil {
		return nil, err
	}

	if stdin != "" {
		in, err := fifo.OpenFifo(ctx, stdin, syscall.O_RDONLY|syscall.O_NONBLOCK, 0)
		if err != nil {
			return nil, err
		}
		cwg.Add(1)
		go func() {
			cwg.Done()
			p := bufPool.Get().(*[]byte)
			defer bufPool.Put(p)
			io.CopyBuffer(epollConsole, in, *p)
		}()
	}

	outw, err := fifo.OpenFifo(ctx, stdout, syscall.O_WRONLY, 0)
	if err != nil {
		return nil, err
	}
	outr, err := fifo.OpenFifo(ctx, stdout, syscall.O_RDONLY, 0)
	if err != nil {
		return nil, err
	}
	wg.Add(1)
	cwg.Add(1)
	go func() {
		cwg.Done()
		p := bufPool.Get().(*[]byte)
		defer bufPool.Put(p)
		io.CopyBuffer(outw, epollConsole, *p)
		epollConsole.Close()
		outr.Close()
		outw.Close()
		wg.Done()
	}()
	return epollConsole, nil
}

func (p *linuxPlatform) ShutdownConsole(ctx context.Context, cons console.Console) error {
	if p.epoller == nil {
		return errors.New("uninitialized epoller")
	}
	epollConsole, ok := cons.(*console.EpollConsole)
	if !ok {
		return errors.Errorf("expected EpollConsole, got %#v", cons)
	}
	return epollConsole.Shutdown(p.epoller.CloseConsole)
}

func (p *linuxPlatform) Close() error {
	return p.epoller.Close()
}

// initialize a single epoll fd to manage our consoles. `initPlatform` should
// only be called once.
func (s *service) initPlatform() error {
	if s.platform != nil {
		return nil
	}
	epoller, err := console.NewEpoller()
	if err != nil {
		return errors.Wrap(err, "failed to initialize epoller")
	}
	s.platform = &linuxPlatform{
		epoller: epoller,
	}
	go epoller.Wait()
	return nil
}
<commit_msg>fix pipe in broken may cause shim lock forever for runtime v2<commit_after>/*
   Copyright The containerd Authors.

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

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

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

package runc

import (
	"context"
	"io"
	"sync"
	"syscall"

	"github.com/containerd/console"
	"github.com/containerd/fifo"
	"github.com/pkg/errors"
)

type linuxPlatform struct {
	epoller *console.Epoller
}

func (p *linuxPlatform) CopyConsole(ctx context.Context, console console.Console, stdin, stdout, stderr string, wg, cwg *sync.WaitGroup) (console.Console, error) {
	if p.epoller == nil {
		return nil, errors.New("uninitialized epoller")
	}

	epollConsole, err := p.epoller.Add(console)
	if err != nil {
		return nil, err
	}

	if stdin != "" {
		in, err := fifo.OpenFifo(ctx, stdin, syscall.O_RDONLY|syscall.O_NONBLOCK, 0)
		if err != nil {
			return nil, err
		}
		cwg.Add(1)
		go func() {
			cwg.Done()
			bp := bufPool.Get().(*[]byte)
			defer bufPool.Put(bp)
			io.CopyBuffer(epollConsole, in, *bp)
			epollConsole.Shutdown(p.epoller.CloseConsole)
		}()
	}

	outw, err := fifo.OpenFifo(ctx, stdout, syscall.O_WRONLY, 0)
	if err != nil {
		return nil, err
	}
	outr, err := fifo.OpenFifo(ctx, stdout, syscall.O_RDONLY, 0)
	if err != nil {
		return nil, err
	}
	wg.Add(1)
	cwg.Add(1)
	go func() {
		cwg.Done()
		p := bufPool.Get().(*[]byte)
		defer bufPool.Put(p)
		io.CopyBuffer(outw, epollConsole, *p)
		epollConsole.Close()
		outr.Close()
		outw.Close()
		wg.Done()
	}()
	return epollConsole, nil
}

func (p *linuxPlatform) ShutdownConsole(ctx context.Context, cons console.Console) error {
	if p.epoller == nil {
		return errors.New("uninitialized epoller")
	}
	epollConsole, ok := cons.(*console.EpollConsole)
	if !ok {
		return errors.Errorf("expected EpollConsole, got %#v", cons)
	}
	return epollConsole.Shutdown(p.epoller.CloseConsole)
}

func (p *linuxPlatform) Close() error {
	return p.epoller.Close()
}

// initialize a single epoll fd to manage our consoles. `initPlatform` should
// only be called once.
func (s *service) initPlatform() error {
	if s.platform != nil {
		return nil
	}
	epoller, err := console.NewEpoller()
	if err != nil {
		return errors.Wrap(err, "failed to initialize epoller")
	}
	s.platform = &linuxPlatform{
		epoller: epoller,
	}
	go epoller.Wait()
	return nil
}
<|endoftext|>


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

package agent

import (
	"fmt"
	"github.com/ossrs/go-oryx/core"
	"net"
)

// the rtmp publish agent,
// to listen at RTMP(tcp://1935) and recv data from RTMP publisher,
// for example, the FMLE publisher.
type RtmpPublish struct {
	wc core.WorkerContainer
	l  net.Listener
}

func NewRtmpPublish(wc core.WorkerContainer) (agent core.Agent) {
	v := &RtmpPublish{
		wc: wc,
	}

	core.Conf.Subscribe(v)

	return v
}

// interface core.Agent
func (v *RtmpPublish) Open() (err error) {
	return v.applyListen(core.Conf)
}

func (v *RtmpPublish) Close() (err error) {
	core.Conf.Unsubscribe(v)
	return v.close()
}

func (v *RtmpPublish) Source() (ss core.Source) {
	return nil
}

func (v *RtmpPublish) Channel() (c chan *core.Message) {
	return nil
}

func (v *RtmpPublish) Sink() (sk core.Sink) {
	return nil
}

func (v *RtmpPublish) close() (err error) {
	if v.l == nil {
		return
	}

	return v.l.Close()
}

func (v *RtmpPublish) applyListen(c *core.Config) (err error) {
	ep := fmt.Sprintf(":%v", c.Listen)

	if v.l, err = net.Listen("tcp", ep); err != nil {
		core.Error.Println("rtmp listen at", ep, "failed. err is", err)
		return
	}
	core.Trace.Println("rtmp listen at", ep)

	return
}

// interface ReloadHandler
func (v *RtmpPublish) OnReloadGlobal(scope int, cc, pc *core.Config) (err error) {
	if scope != core.ReloadListen {
		return
	}

	if err = v.close(); err != nil {
		return
	}

	if err = v.applyListen(cc); err != nil {
		return
	}

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

package agent

import (
	"fmt"
	"github.com/ossrs/go-oryx/core"
	"net"
)

// the rtmp publish agent,
// to listen at RTMP(tcp://1935) and recv data from RTMP publisher,
// for example, the FMLE publisher.
type RtmpPublish struct {
	endpoint string
	wc       core.WorkerContainer
	l        net.Listener
}

func NewRtmpPublish(wc core.WorkerContainer) (agent core.Agent) {
	v := &RtmpPublish{
		wc: wc,
	}

	core.Conf.Subscribe(v)

	return v
}

// interface core.Agent
func (v *RtmpPublish) Open() (err error) {
	return v.applyListen(core.Conf)
}

func (v *RtmpPublish) Close() (err error) {
	core.Conf.Unsubscribe(v)
	return v.close()
}

func (v *RtmpPublish) Source() (ss core.Source) {
	return nil
}

func (v *RtmpPublish) Channel() (c chan *core.Message) {
	return nil
}

func (v *RtmpPublish) Sink() (sk core.Sink) {
	return nil
}

func (v *RtmpPublish) close() (err error) {
	if v.l == nil {
		return
	}

	if err = v.l.Close(); err != nil {
		core.Error.Println("close rtmp listener failed. err is", err)
		return
	}

	core.Trace.Println("close rtmp listen", v.endpoint, "ok")
	return
}

func (v *RtmpPublish) applyListen(c *core.Config) (err error) {
	v.endpoint = fmt.Sprintf(":%v", c.Listen)

	ep := v.endpoint
	if v.l, err = net.Listen("tcp", ep); err != nil {
		core.Error.Println("rtmp listen at", ep, "failed. err is", err)
		return
	}
	core.Trace.Println("rtmp listen at", ep)

	return
}

// interface ReloadHandler
func (v *RtmpPublish) OnReloadGlobal(scope int, cc, pc *core.Config) (err error) {
	if scope != core.ReloadListen {
		return
	}

	if err = v.close(); err != nil {
		return
	}

	if err = v.applyListen(cc); err != nil {
		return
	}

	return
}
<|endoftext|>


<commit_before>package typeinfer

// Context captures variables (and invariants) of scopes during execution.
// Different contexts are used for different level of fine-grainedness.

import (
	"bytes"
	"fmt"
	"go/types"
	"log"

	"github.com/nickng/dingo-hunter/typeinfer/migo"
	"golang.org/x/tools/go/ssa"
)

// LoopState indicate the state (in analysis) of loop currently in.
type LoopState int

//go:generate stringer -type=LoopState
const (
	NonLoop LoopState = iota
	Enter             // Loop initialisation and condition checking
	Body              // Loop body (repeat)
	Exit              // Loop exit
)

// LoopBound indicates if a loop is bounded or not.
type LoopBound int

//go:generate stringer -type=LoopBound
const (
	Unknown LoopBound = iota
	Static            // Static loop.
	Dynamic           // Dynamic loop.
)

// Program captures the program environment.
//
// A single inference has exactly one Program, and it contains all global
// data (and metadata) in the program.
type Program struct {
	FuncInstance map[*ssa.Function]int // Count number of function instances.
	InitPkgs     map[*ssa.Package]bool // Initialised packages.
	Infer        *TypeInfer            // Reference to inference.
	MigoProg     *migo.Program         // Core calculus of program.

	arrays   map[VarInstance]Elems     // Array elements.
	closures map[VarInstance]Captures  // Closures.
	globals  map[ssa.Value]VarInstance // Global variables.
	structs  map[VarInstance]Fields    // Heap allocated struct fields.
}

// NewProgram creates a program for a type inference.
func NewProgram(infer *TypeInfer) *Program {
	return &Program{
		FuncInstance: make(map[*ssa.Function]int),
		InitPkgs:     make(map[*ssa.Package]bool),
		Infer:        infer,
		arrays:       make(map[VarInstance]Elems),
		closures:     make(map[VarInstance]Captures),
		globals:      make(map[ssa.Value]VarInstance),
		structs:      make(map[VarInstance]Fields),
	}
}

// Function captures the function environment.
//
// Function environment stores local variable instances (as reference), return
// values, if-then-else parent, select-condition.
type Function struct {
	Fn      *ssa.Function           // Function callee (this).
	Caller  *Function               // Function caller (parent).
	Prog    *Program                // Program environment (global).
	Visited map[*ssa.BasicBlock]int // Visited block tracking.
	Level   int                     // Call level (for indentation).
	FuncDef *migo.Function          // Function definition.

	id        int                                         // Instance identifier.
	hasBody   bool                                        // True if function has body.
	arrays    map[VarInstance]Elems                       // Array elements.
	commaok   map[VarInstance]*CommaOk                    // CommaOK statements.
	defers    []*ssa.Defer                                // Deferred calls.
	locals    map[ssa.Value]VarInstance                   // Local variable instances.
	maps      map[VarInstance]map[VarInstance]VarInstance // Map instances (just an approximate).
	retvals   []VarInstance                               // Return value instances.
	selects   map[VarInstance]*Select                     // Select cases mapping.
	structs   map[VarInstance]Fields                      // Stack allocated struct fields.
	tuples    map[VarInstance]Tuples                      // Tuples.
	loopstack *LoopStack                                  // Stack of Loop.
}

// NewMainFunction returns a new main() call context.
func NewMainFunction(prog *Program, mainFn *ssa.Function) *Function {
	return &Function{
		Fn:      mainFn,
		Prog:    prog,
		Visited: make(map[*ssa.BasicBlock]int),
		FuncDef: migo.NewFunction("main.main"),

		arrays:    make(map[VarInstance]Elems),
		commaok:   make(map[VarInstance]*CommaOk),
		defers:    []*ssa.Defer{},
		locals:    make(map[ssa.Value]VarInstance),
		maps:      make(map[VarInstance]map[VarInstance]VarInstance),
		retvals:   []VarInstance{},
		selects:   make(map[VarInstance]*Select),
		structs:   make(map[VarInstance]Fields),
		tuples:    make(map[VarInstance]Tuples),
		loopstack: NewLoopStack(),
	}
}

// NewFunction returns a new function call context, and takes the caller's
// context as parameter.
func NewFunction(caller *Function) *Function {
	return &Function{
		Caller:  caller,
		Prog:    caller.Prog,
		Visited: make(map[*ssa.BasicBlock]int),
		FuncDef: migo.NewFunction("__uninitialised__"),
		Level:   caller.Level + 1,

		arrays:    make(map[VarInstance]Elems),
		commaok:   make(map[VarInstance]*CommaOk),
		defers:    []*ssa.Defer{},
		locals:    make(map[ssa.Value]VarInstance),
		maps:      make(map[VarInstance]map[VarInstance]VarInstance),
		retvals:   []VarInstance{},
		selects:   make(map[VarInstance]*Select),
		structs:   make(map[VarInstance]Fields),
		tuples:    make(map[VarInstance]Tuples),
		loopstack: NewLoopStack(),
	}
}

// HasBody returns true if Function is user-defined or has source code and
// built in SSA program.
func (caller *Function) HasBody() bool { return caller.hasBody }

// prepareCallFn prepares a caller Function to visit performing necessary context switching and returns a new callee Function.
// rcvr is non-nil if invoke call
func (caller *Function) prepareCallFn(common *ssa.CallCommon, fn *ssa.Function, rcvr ssa.Value) *Function {
	callee := NewFunction(caller)
	callee.Fn = fn
	// This function was called before
	if _, ok := callee.Prog.FuncInstance[callee.Fn]; ok {
		callee.Prog.FuncInstance[callee.Fn]++
	} else {
		callee.Prog.FuncInstance[callee.Fn] = 0
	}
	callee.FuncDef.Name = fn.String()
	callee.id = callee.Prog.FuncInstance[callee.Fn]
	for i, param := range callee.Fn.Params {
		var argCaller ssa.Value
		if rcvr != nil {
			if i == 0 {
				argCaller = rcvr
			} else {
				argCaller = common.Args[i-1]
			}
		} else {
			argCaller = common.Args[i]
		}
		if _, ok := argCaller.Type().(*types.Chan); ok {
			callee.FuncDef.AddParams(&migo.Parameter{Caller: argCaller, Callee: param})
		}
		if inst, ok := caller.locals[argCaller]; ok {
			callee.locals[param] = inst

			// Copy array and struct from parent.
			if elems, ok := caller.arrays[inst]; ok {
				callee.arrays[inst] = elems
			}
			if fields, ok := caller.structs[inst]; ok {
				callee.structs[inst] = fields
			}
			if maps, ok := caller.maps[inst]; ok {
				callee.maps[inst] = maps
			}
		} else if c, ok := argCaller.(*ssa.Const); ok {
			callee.locals[param] = &ConstInstance{c}
		}
	}

	if inst, ok := caller.locals[common.Value]; ok {
		if cap, ok := caller.Prog.closures[inst]; ok {
			for i, fv := range callee.Fn.FreeVars {
				callee.locals[fv] = cap[i]
			}
		}
	}
	return callee
}

// InstanceID returns the current function instance number (numbers of times
// function called).
func (caller *Function) InstanceID() int {
	if caller.id < 0 {
		log.Fatal(ErrUnitialisedFunc)
	}
	return caller.id
}

func (caller *Function) String() string {
	var buf bytes.Buffer
	buf.WriteString("--- Context ---\n")
	if caller.Fn == nil {
		log.Fatal(ErrUnitialisedFunc)
	}
	buf.WriteString(fmt.Sprintf("\t- Fn:\t%s_%d\n", caller.Fn, caller.id))
	if caller.Caller != nil {
		buf.WriteString(fmt.Sprintf("\t- Parent:\t%s\n", caller.Caller.Fn.String()))
	} else {
		buf.WriteString("\t- Parent: main.main\n")
	}
	for val, instance := range caller.locals {
		buf.WriteString(fmt.Sprintf("\t\t- %s = %s\n", val.Name(), instance))
	}
	buf.WriteString(fmt.Sprintf("\t- Retvals: %d\n", len(caller.retvals)))
	return buf.String()
}

func (caller *Function) updateInstances(old, new VarInstance) {
	for inst, array := range caller.arrays {
		for k, v := range array {
			if v == old {
				caller.arrays[inst][k] = new
			}
		}
	}
	for inst, array := range caller.Prog.arrays {
		for k, v := range array {
			if v == old {
				caller.Prog.arrays[inst][k] = new
			}
		}
	}
	for inst, struc := range caller.structs {
		for i, field := range struc {
			if field == old {
				caller.structs[inst][i] = new
			}
		}
	}
	for inst, struc := range caller.Prog.structs {
		for i, field := range struc {
			if field == old {
				caller.Prog.structs[inst][i] = new
			}
		}
	}
	for inst, mmap := range caller.maps {
		for k, v := range mmap {
			if v == old {
				caller.maps[inst][k] = new
			}
		}
	}
}

// Block captures information about SSA block.
type Block struct {
	Function *Function      // Parent function context.
	MigoDef  *migo.Function // MiGo Function for the block.
	Pred     int            // Immediate predecessor trace.
	Index    int            // Current block index.
}

// NewBlock creates a new block enclosed by the given function.
func NewBlock(parent *Function, block *ssa.BasicBlock, curr int) *Block {
	blockFn := fmt.Sprintf("%s#%d", parent.Fn.String(), block.Index)
	parent.ChildBlocks[block.Index] = &Block{
		Function: parent,
		MigoDef:  migo.NewFunction(blockFn),
		Pred:     curr,
		Index:    block.Index,
	}
	return parent.ChildBlocks[block.Index]
}
<commit_msg>ChildBlocks<commit_after>package typeinfer

// Context captures variables (and invariants) of scopes during execution.
// Different contexts are used for different level of fine-grainedness.

import (
	"bytes"
	"fmt"
	"go/types"
	"log"

	"github.com/nickng/dingo-hunter/typeinfer/migo"
	"golang.org/x/tools/go/ssa"
)

// LoopState indicate the state (in analysis) of loop currently in.
type LoopState int

//go:generate stringer -type=LoopState
const (
	NonLoop LoopState = iota
	Enter             // Loop initialisation and condition checking
	Body              // Loop body (repeat)
	Exit              // Loop exit
)

// LoopBound indicates if a loop is bounded or not.
type LoopBound int

//go:generate stringer -type=LoopBound
const (
	Unknown LoopBound = iota
	Static            // Static loop.
	Dynamic           // Dynamic loop.
)

// Program captures the program environment.
//
// A single inference has exactly one Program, and it contains all global
// data (and metadata) in the program.
type Program struct {
	FuncInstance map[*ssa.Function]int // Count number of function instances.
	InitPkgs     map[*ssa.Package]bool // Initialised packages.
	Infer        *TypeInfer            // Reference to inference.
	MigoProg     *migo.Program         // Core calculus of program.

	arrays   map[VarInstance]Elems     // Array elements.
	closures map[VarInstance]Captures  // Closures.
	globals  map[ssa.Value]VarInstance // Global variables.
	structs  map[VarInstance]Fields    // Heap allocated struct fields.
}

// NewProgram creates a program for a type inference.
func NewProgram(infer *TypeInfer) *Program {
	return &Program{
		FuncInstance: make(map[*ssa.Function]int),
		InitPkgs:     make(map[*ssa.Package]bool),
		Infer:        infer,
		arrays:       make(map[VarInstance]Elems),
		closures:     make(map[VarInstance]Captures),
		globals:      make(map[ssa.Value]VarInstance),
		structs:      make(map[VarInstance]Fields),
	}
}

// Function captures the function environment.
//
// Function environment stores local variable instances (as reference), return
// values, if-then-else parent, select-condition.
type Function struct {
	Fn          *ssa.Function           // Function callee (this).
	Caller      *Function               // Function caller (parent).
	Prog        *Program                // Program environment (global).
	Visited     map[*ssa.BasicBlock]int // Visited block tracking.
	Level       int                     // Call level (for indentation).
	FuncDef     *migo.Function          // Function definition.
	ChildBlocks map[int]*Block          // Map from index -> child SSA blocks.

	id        int                                         // Instance identifier.
	hasBody   bool                                        // True if function has body.
	arrays    map[VarInstance]Elems                       // Array elements.
	commaok   map[VarInstance]*CommaOk                    // CommaOK statements.
	defers    []*ssa.Defer                                // Deferred calls.
	locals    map[ssa.Value]VarInstance                   // Local variable instances.
	maps      map[VarInstance]map[VarInstance]VarInstance // Map instances (just an approximate).
	retvals   []VarInstance                               // Return value instances.
	selects   map[VarInstance]*Select                     // Select cases mapping.
	structs   map[VarInstance]Fields                      // Stack allocated struct fields.
	tuples    map[VarInstance]Tuples                      // Tuples.
	loopstack *LoopStack                                  // Stack of Loop.
}

// NewMainFunction returns a new main() call context.
func NewMainFunction(prog *Program, mainFn *ssa.Function) *Function {
	return &Function{
		Fn:          mainFn,
		Prog:        prog,
		Visited:     make(map[*ssa.BasicBlock]int),
		FuncDef:     migo.NewFunction("main.main"),
		ChildBlocks: make(map[int]*Block),

		arrays:    make(map[VarInstance]Elems),
		commaok:   make(map[VarInstance]*CommaOk),
		defers:    []*ssa.Defer{},
		locals:    make(map[ssa.Value]VarInstance),
		maps:      make(map[VarInstance]map[VarInstance]VarInstance),
		retvals:   []VarInstance{},
		selects:   make(map[VarInstance]*Select),
		structs:   make(map[VarInstance]Fields),
		tuples:    make(map[VarInstance]Tuples),
		loopstack: NewLoopStack(),
	}
}

// NewFunction returns a new function call context, and takes the caller's
// context as parameter.
func NewFunction(caller *Function) *Function {
	return &Function{
		Caller:      caller,
		Prog:        caller.Prog,
		Visited:     make(map[*ssa.BasicBlock]int),
		FuncDef:     migo.NewFunction("__uninitialised__"),
		Level:       caller.Level + 1,
		ChildBlocks: make(map[int]*Block),

		arrays:    make(map[VarInstance]Elems),
		commaok:   make(map[VarInstance]*CommaOk),
		defers:    []*ssa.Defer{},
		locals:    make(map[ssa.Value]VarInstance),
		maps:      make(map[VarInstance]map[VarInstance]VarInstance),
		retvals:   []VarInstance{},
		selects:   make(map[VarInstance]*Select),
		structs:   make(map[VarInstance]Fields),
		tuples:    make(map[VarInstance]Tuples),
		loopstack: NewLoopStack(),
	}
}

// HasBody returns true if Function is user-defined or has source code and
// built in SSA program.
func (caller *Function) HasBody() bool { return caller.hasBody }

// prepareCallFn prepares a caller Function to visit performing necessary context switching and returns a new callee Function.
// rcvr is non-nil if invoke call
func (caller *Function) prepareCallFn(common *ssa.CallCommon, fn *ssa.Function, rcvr ssa.Value) *Function {
	callee := NewFunction(caller)
	callee.Fn = fn
	// This function was called before
	if _, ok := callee.Prog.FuncInstance[callee.Fn]; ok {
		callee.Prog.FuncInstance[callee.Fn]++
	} else {
		callee.Prog.FuncInstance[callee.Fn] = 0
	}
	callee.FuncDef.Name = fn.String()
	callee.id = callee.Prog.FuncInstance[callee.Fn]
	for i, param := range callee.Fn.Params {
		var argCaller ssa.Value
		if rcvr != nil {
			if i == 0 {
				argCaller = rcvr
			} else {
				argCaller = common.Args[i-1]
			}
		} else {
			argCaller = common.Args[i]
		}
		if _, ok := argCaller.Type().(*types.Chan); ok {
			callee.FuncDef.AddParams(&migo.Parameter{Caller: argCaller, Callee: param})
		}
		if inst, ok := caller.locals[argCaller]; ok {
			callee.locals[param] = inst

			// Copy array and struct from parent.
			if elems, ok := caller.arrays[inst]; ok {
				callee.arrays[inst] = elems
			}
			if fields, ok := caller.structs[inst]; ok {
				callee.structs[inst] = fields
			}
			if maps, ok := caller.maps[inst]; ok {
				callee.maps[inst] = maps
			}
		} else if c, ok := argCaller.(*ssa.Const); ok {
			callee.locals[param] = &ConstInstance{c}
		}
	}

	if inst, ok := caller.locals[common.Value]; ok {
		if cap, ok := caller.Prog.closures[inst]; ok {
			for i, fv := range callee.Fn.FreeVars {
				callee.locals[fv] = cap[i]
			}
		}
	}
	return callee
}

// InstanceID returns the current function instance number (numbers of times
// function called).
func (caller *Function) InstanceID() int {
	if caller.id < 0 {
		log.Fatal(ErrUnitialisedFunc)
	}
	return caller.id
}

func (caller *Function) String() string {
	var buf bytes.Buffer
	buf.WriteString("--- Context ---\n")
	if caller.Fn == nil {
		log.Fatal(ErrUnitialisedFunc)
	}
	buf.WriteString(fmt.Sprintf("\t- Fn:\t%s_%d\n", caller.Fn, caller.id))
	if caller.Caller != nil {
		buf.WriteString(fmt.Sprintf("\t- Parent:\t%s\n", caller.Caller.Fn.String()))
	} else {
		buf.WriteString("\t- Parent: main.main\n")
	}
	for val, instance := range caller.locals {
		buf.WriteString(fmt.Sprintf("\t\t- %s = %s\n", val.Name(), instance))
	}
	buf.WriteString(fmt.Sprintf("\t- Retvals: %d\n", len(caller.retvals)))
	return buf.String()
}

func (caller *Function) updateInstances(old, new VarInstance) {
	for inst, array := range caller.arrays {
		for k, v := range array {
			if v == old {
				caller.arrays[inst][k] = new
			}
		}
	}
	for inst, array := range caller.Prog.arrays {
		for k, v := range array {
			if v == old {
				caller.Prog.arrays[inst][k] = new
			}
		}
	}
	for inst, struc := range caller.structs {
		for i, field := range struc {
			if field == old {
				caller.structs[inst][i] = new
			}
		}
	}
	for inst, struc := range caller.Prog.structs {
		for i, field := range struc {
			if field == old {
				caller.Prog.structs[inst][i] = new
			}
		}
	}
	for inst, mmap := range caller.maps {
		for k, v := range mmap {
			if v == old {
				caller.maps[inst][k] = new
			}
		}
	}
}

// Block captures information about SSA block.
type Block struct {
	Function *Function      // Parent function context.
	MigoDef  *migo.Function // MiGo Function for the block.
	Pred     int            // Immediate predecessor trace.
	Index    int            // Current block index.
}

// NewBlock creates a new block enclosed by the given function.
func NewBlock(parent *Function, block *ssa.BasicBlock, curr int) *Block {
	blockFn := fmt.Sprintf("%s#%d", parent.Fn.String(), block.Index)
	parent.ChildBlocks[block.Index] = &Block{
		Function: parent,
		MigoDef:  migo.NewFunction(blockFn),
		Pred:     curr,
		Index:    block.Index,
	}
	return parent.ChildBlocks[block.Index]
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/HewlettPackard/oneview-golang/ov"
	"os"
	"time"
)

func main() {

	var (
		ClientOV       *ov.OVClient
		name_to_create = "VolumeTemplateExample"
		name_to_update = "VolumeTemplateExample- updated"
	)

	ovc := ClientOV.NewOVClient(
		os.Getenv("ONEVIEW_OV_USER"),
		os.Getenv("ONEVIEW_OV_PASSWORD"),
		os.Getenv("ONEVIEW_OV_DOMAIN"),
		os.Getenv("ONEVIEW_OV_ENDPOINT"),
		false,
		1000,
		"*")

	name_properties := ov.TemplatePropertyDatatypeStruct{
		Required:    true,
		Type:        "string",
		Title:       "Volume name",
		Description: "A volume name between 1 and 100 characters",
		Maxlength:   100,
		Minlength:   1,
		Meta: ov.Meta{
			Locked: false,
		},
	}

	storage_pool_properties := ov.TemplatePropertyDatatypeStruct{
		Required:    true,
		Type:        "string",
		Title:       "Storage Pool",
		Description: "StoragePoolURI the volume should be added to",
		Default:     "/rest/storage-pools/F693B0B6-AD80-40C0-935D-AA99009ED046",
		Meta: ov.Meta{
			Locked:       false,
			CreateOnly:   true,
			SemanticType: "device-storage-pool",
		},
		Format: "x-uri-reference",
	}

	size_properties := ov.TemplatePropertyDatatypeStructInt{
		Required:    true,
		Type:        "integer",
		Title:       "Capacity",
		Default:     1073741824,
		Minimum:     4194304,
		Description: "Capacity of the volume in bytes",
		Meta: ov.Meta{
			Locked:       false,
			SemanticType: "capacity",
		},
	}

	dataProtectionLevel_properties := ov.TemplatePropertyDatatypeStruct{
		Required: true,
		Type:     "string",
		Enum: []string{"NetworkRaid0None",
			"NetworkRaid5SingleParity",
			"NetworkRaid10Mirror2Way",
			"NetworkRaid10Mirror3Way",
			"NetworkRaid10Mirror4Way",
			"NetworkRaid6DualParity",
		},
		Title:       "Data Protection Level",
		Default:     "NetworkRaid10Mirror2Way",
		Description: "Indicates the number and configuration of data copies in the Storage Pool",
		Meta: ov.Meta{
			Locked:       false,
			SemanticType: "device-dataProtectionLevel",
		},
	}

	template_version_properties := ov.TemplatePropertyDatatypeStruct{
		Required:    true,
		Type:        "string",
		Title:       "Template version",
		Description: "Version of the template",
		Default:     "1.1",
		Meta: ov.Meta{
			Locked: true,
		},
	}

	description_properties := ov.TemplatePropertyDatatypeStruct{
		Required:    false,
		Type:        "string",
		Title:       "Description",
		Description: "A description for the volume",
		Default:     "A description for the volume",
		Maxlength:   2000,
		Minlength:   1,
		Meta: ov.Meta{
			Locked: false,
		},
	}

	provisioning_type_properties := ov.TemplatePropertyDatatypeStruct{
		Required:    false,
		Title:       "Provisioning Type",
		Type:        "string",
		Description: "The provisioning type for the volume",
		Default:     "Thin",
		Enum:        []string{"Thin", "Full"},
		Meta: ov.Meta{
			Locked:       true,
			CreateOnly:   true,
			SemanticType: "device-provisioningType",
		},
	}

	adaptive_optimization_properties := ov.TemplatePropertyDatatypeStructBool{
		Meta: ov.Meta{
			Locked: true,
		},
		Type:        "boolean",
		Description: "",
		Default:     true,
		Required:    false,
		Title:       "Adaptive Optimization",
	}

	is_shareable_properties := ov.TemplatePropertyDatatypeStructBool{
		Meta: ov.Meta{
			Locked: true,
		},
		Type:        "boolean",
		Description: "The shareability of the volume",
		Default:     true,
		Required:    false,
		Title:       "Is Shareable",
	}

	Properties := ov.TemplateProperties{
		Name:                          &name_properties,
		StoragePool:                   &storage_pool_properties,
		Size:                          &size_properties,
		DataProtectionLevel:           &dataProtectionLevel_properties,
		TemplateVersion:               &template_version_properties,
		Description:                   &description_properties,
		ProvisioningType:              &provisioning_type_properties,
		IsAdaptiveOptimizationEnabled: &adaptive_optimization_properties,
		IsShareable:                   &is_shareable_properties,
	}

	storageVolumeTemplate := ov.StorageVolumeTemplate{
		TemplateProperties: Properties,
		Name:               name_to_create,
		Description:        "Volume template Example",
		RootTemplateUri:    "/rest/storage-volume-templates/533c5b9e-26c3-4c2e-af4c-aa99009ed20e",
	}

	err := ovc.CreateStorageVolumeTemplate(storageVolumeTemplate)
	if err != nil {
		fmt.Println("Could not create the volume Template", err)
	} else {
		fmt.Println("Volume Template created successfully", storageVolumeTemplate.Name)
	}

	// Update the given storage volume
	update_vol_template, _ := ovc.GetStorageVolumeTemplateByName(name_to_create)
	update_vol_template.Name = name_to_update
	update_vol_template.Description = "Updating description"

	err = ovc.UpdateStorageVolumeTemplate(update_vol_template)
	if err != nil {
		fmt.Println("Could not update the volume template", err)
	} else {
		fmt.Println("Volume template updated")
	}
	time.Sleep(2 * time.Second)

	// Get All the volume templates present
	fmt.Println("\nGetting all the storage volume templates present in the system: \n")
	sort := "name:desc"
	vol_temp_list, err := ovc.GetStorageVolumeTemplates("", sort, "", "")
	if err != nil {
		fmt.Println("Error Getting the storage volume templates ", err)
	}
	for i := 0; i < len(vol_temp_list.Members); i++ {
		fmt.Println(vol_temp_list.Members[i].Name)
	}

	/*	// Get volume by name
		fmt.Println("\nGetting details of volume with name: ", name_to_update)
		vol_by_name, _ := ovc.GetStorageVolumeTemplateByName(name_to_update)
		fmt.Println(vol_by_name.URI)
	*/
	// Delete the created volume
	fmt.Println("\nDeleting the volume with name : ", name_to_update)
	err = ovc.DeleteStorageVolumeTemplate(name_to_update)
	if err != nil {
		fmt.Println("Delete Unsuccessful", err)
	}

}
<commit_msg>add comments to example file<commit_after>package main

import (
	"fmt"
	"github.com/HewlettPackard/oneview-golang/ov"
	"os"
	"time"
)

func main() {

	var (
		ClientOV       *ov.OVClient
		name_to_create = "VolumeTemplateExample"
		name_to_update = "VolumeTemplateExample- updated"
	)

	ovc := ClientOV.NewOVClient(
		os.Getenv("ONEVIEW_OV_USER"),
		os.Getenv("ONEVIEW_OV_PASSWORD"),
		os.Getenv("ONEVIEW_OV_DOMAIN"),
		os.Getenv("ONEVIEW_OV_ENDPOINT"),
		false,
		1000,
		"*")

	name_properties := ov.TemplatePropertyDatatypeStruct{
		Required:    true,
		Type:        "string",
		Title:       "Volume name",
		Description: "A volume name between 1 and 100 characters",
		Maxlength:   100,
		Minlength:   1,
		Meta: ov.Meta{
			Locked: false,
		},
	}

	storage_pool_properties := ov.TemplatePropertyDatatypeStruct{
		Required:    true,
		Type:        "string",
		Title:       "Storage Pool",
		Description: "StoragePoolURI the volume should be added to",
		Default:     "/rest/storage-pools/F693B0B6-AD80-40C0-935D-AA99009ED046",
		Meta: ov.Meta{
			Locked:       false,
			CreateOnly:   true,
			SemanticType: "device-storage-pool",
		},
		Format: "x-uri-reference",
	}

	size_properties := ov.TemplatePropertyDatatypeStructInt{
		Required:    true,
		Type:        "integer",
		Title:       "Capacity",
		Default:     1073741824,
		Minimum:     4194304,
		Description: "Capacity of the volume in bytes",
		Meta: ov.Meta{
			Locked:       false,
			SemanticType: "capacity",
		},
	}

	dataProtectionLevel_properties := ov.TemplatePropertyDatatypeStruct{
		Required: true,
		Type:     "string",
		Enum: []string{"NetworkRaid0None",
			"NetworkRaid5SingleParity",
			"NetworkRaid10Mirror2Way",
			"NetworkRaid10Mirror3Way",
			"NetworkRaid10Mirror4Way",
			"NetworkRaid6DualParity",
		},
		Title:       "Data Protection Level",
		Default:     "NetworkRaid10Mirror2Way",
		Description: "Indicates the number and configuration of data copies in the Storage Pool",
		Meta: ov.Meta{
			Locked:       false,
			SemanticType: "device-dataProtectionLevel",
		},
	}

	template_version_properties := ov.TemplatePropertyDatatypeStruct{
		Required:    true,
		Type:        "string",
		Title:       "Template version",
		Description: "Version of the template",
		Default:     "1.1",
		Meta: ov.Meta{
			Locked: true,
		},
	}

	description_properties := ov.TemplatePropertyDatatypeStruct{
		Required:    false,
		Type:        "string",
		Title:       "Description",
		Description: "A description for the volume",
		Default:     "A description for the volume",
		Maxlength:   2000,
		Minlength:   1,
		Meta: ov.Meta{
			Locked: false,
		},
	}

	provisioning_type_properties := ov.TemplatePropertyDatatypeStruct{
		Required:    false,
		Title:       "Provisioning Type",
		Type:        "string",
		Description: "The provisioning type for the volume",
		Default:     "Thin",
		Enum:        []string{"Thin", "Full"},
		Meta: ov.Meta{
			Locked:       true,
			CreateOnly:   true,
			SemanticType: "device-provisioningType",
		},
	}

	adaptive_optimization_properties := ov.TemplatePropertyDatatypeStructBool{
		Meta: ov.Meta{
			Locked: true,
		},
		Type:        "boolean",
		Description: "",
		Default:     true,
		Required:    false,
		Title:       "Adaptive Optimization",
	}

	is_shareable_properties := ov.TemplatePropertyDatatypeStructBool{
		Meta: ov.Meta{
			Locked: true,
		},
		Type:        "boolean",
		Description: "The shareability of the volume",
		Default:     true,
		Required:    false,
		Title:       "Is Shareable",
	}

	Properties := ov.TemplateProperties{
		Name:                          &name_properties,
		StoragePool:                   &storage_pool_properties,
		Size:                          &size_properties,
		DataProtectionLevel:           &dataProtectionLevel_properties,
		TemplateVersion:               &template_version_properties,
		Description:                   &description_properties,
		ProvisioningType:              &provisioning_type_properties,
		IsAdaptiveOptimizationEnabled: &adaptive_optimization_properties,
		IsShareable:                   &is_shareable_properties,
	}

	storageVolumeTemplate := ov.StorageVolumeTemplate{
		TemplateProperties: Properties,
		Name:               name_to_create,
		Description:        "Volume template Example",
		RootTemplateUri:    "/rest/storage-volume-templates/533c5b9e-26c3-4c2e-af4c-aa99009ed20e",
	}

	err := ovc.CreateStorageVolumeTemplate(storageVolumeTemplate)
	if err != nil {
		fmt.Println("Could not create the volume Template", err)
	} else {
		fmt.Println("Volume Template created successfully", storageVolumeTemplate.Name)
	}

	// Get the volume template by name
	update_vol_template, _ := ovc.GetStorageVolumeTemplateByName(name_to_create)
	update_vol_template.Name = name_to_update
	update_vol_template.Description = "Updating description"

	// Update the previously created storage volume template
	err = ovc.UpdateStorageVolumeTemplate(update_vol_template)
	if err != nil {
		fmt.Println("Could not update the volume template", err)
	} else {
		fmt.Println("Volume template updated")
	}
	time.Sleep(2 * time.Second)

	// Get All the volume templates present
	fmt.Println("\nGetting all the storage volume templates present in the system: \n")
	sort := "name:desc"
	vol_temp_list, err := ovc.GetStorageVolumeTemplates("", sort, "", "")
	if err != nil {
		fmt.Println("Error Getting the storage volume templates ", err)
	}
	for i := 0; i < len(vol_temp_list.Members); i++ {
		fmt.Println(vol_temp_list.Members[i].Name)
	}

	// Delete the created volume template
	fmt.Println("\nDeleting the volume with name : ", name_to_update)
	err = ovc.DeleteStorageVolumeTemplate(name_to_update)
	if err != nil {
		fmt.Println("Delete Unsuccessful", err)
	}

}
<|endoftext|>


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

package cbft

import (
	"encoding/json"
	"fmt"
	"io"
	"math/rand"
	"strings"
	"sync"
	"sync/atomic"

	"github.com/couchbase/gomemcached"
	log "github.com/couchbaselabs/clog"
	"github.com/couchbaselabs/go-couchbase"
	"github.com/couchbaselabs/go-couchbase/cbdatasource"
)

func init() {
	RegisterFeedType("couchbase", &FeedType{
		Start:       StartDCPFeed,
		Partitions:  CouchbasePartitions,
		Public:      true,
		Description: "couchbase - Couchbase Server/Cluster data source",
		StartSample: NewDCPFeedParams(),
	})
	RegisterFeedType("couchbase-dcp", &FeedType{
		Start:       StartDCPFeed,
		Partitions:  CouchbasePartitions,
		Public:      false, // Won't be listed in /api/managerMeta output.
		Description: "couchbase-dcp - Couchbase Server/Cluster data source, via DCP protocol",
		StartSample: NewDCPFeedParams(),
	})
}

func StartDCPFeed(mgr *Manager, feedName, indexName, indexUUID,
	sourceType, bucketName, bucketUUID, params string, dests map[string]Dest) error {
	feed, err := NewDCPFeed(feedName, mgr.server, "default",
		bucketName, bucketUUID, params, BasicPartitionFunc, dests)
	if err != nil {
		return fmt.Errorf("feed_dcp: could not prepare DCP feed to server: %s,"+
			" bucketName: %s, indexName: %s, err: %v",
			mgr.server, bucketName, indexName, err)
	}
	err = feed.Start()
	if err != nil {
		return fmt.Errorf("feed_dcp: could not start dcp feed, server: %s, err: %v",
			mgr.server, err)
	}
	err = mgr.registerFeed(feed)
	if err != nil {
		feed.Close()
		return err
	}
	return nil
}

// A DCPFeed implements both Feed and cbdatasource.Receiver interfaces.
type DCPFeed struct {
	name       string
	url        string
	poolName   string
	bucketName string
	bucketUUID string
	params     *DCPFeedParams
	pf         DestPartitionFunc
	dests      map[string]Dest
	bds        cbdatasource.BucketDataSource

	m       sync.Mutex
	closed  bool
	lastErr error
	stats   *DestStats
}

type DCPFeedParams struct {
	AuthUser     string `json:"authUser"` // May be "" for no auth.
	AuthPassword string `json:"authPassword"`

	// Factor (like 1.5) to increase sleep time between retries
	// in connecting to a cluster manager node.
	ClusterManagerBackoffFactor float32 `json:"clusterManagerBackoffFactor"`

	// Initial sleep time (millisecs) before first retry to cluster manager.
	ClusterManagerSleepInitMS int `json:"clusterManagerSleepInitMS"`

	// Maximum sleep time (millisecs) between retries to cluster manager.
	ClusterManagerSleepMaxMS int `json:"clusterManagerSleepMaxMS"`

	// Factor (like 1.5) to increase sleep time between retries
	// in connecting to a data manager node.
	DataManagerBackoffFactor float32 `json:"dataManagerBackoffFactor"`

	// Initial sleep time (millisecs) before first retry to data manager.
	DataManagerSleepInitMS int `json:"dataManagerSleepInitMS"`

	// Maximum sleep time (millisecs) between retries to data manager.
	DataManagerSleepMaxMS int `json:"dataManagerSleepMaxMS"`

	// Buffer size in bytes provided for UPR flow control.
	FeedBufferSizeBytes uint32 `json:"feedBufferSizeBytes"`

	// Used for UPR flow control and buffer-ack messages when this
	// percentage of FeedBufferSizeBytes is reached.
	FeedBufferAckThreshold float32 `json:"feedBufferAckThreshold"`
}

func NewDCPFeedParams() *DCPFeedParams {
	return &DCPFeedParams{
		ClusterManagerSleepMaxMS: 20000,
		DataManagerSleepMaxMS:    20000,
	}
}

func (d *DCPFeedParams) GetCredentials() (string, string, string) {
	// TODO: bucketName not necessarily userName.
	return d.AuthUser, d.AuthPassword, d.AuthUser
}

func NewDCPFeed(name, url, poolName, bucketName, bucketUUID, paramsStr string,
	pf DestPartitionFunc, dests map[string]Dest) (*DCPFeed, error) {
	params := NewDCPFeedParams()
	if paramsStr != "" {
		err := json.Unmarshal([]byte(paramsStr), params)
		if err != nil {
			return nil, err
		}
	}

	vbucketIds, err := ParsePartitionsToVBucketIds(dests)
	if err != nil {
		return nil, err
	}
	if len(vbucketIds) <= 0 {
		vbucketIds = nil
	}

	var auth couchbase.AuthHandler
	if params.AuthUser != "" {
		auth = params
	}

	options := &cbdatasource.BucketDataSourceOptions{
		Name: fmt.Sprintf("%s-%x", name, rand.Int31()),
		ClusterManagerBackoffFactor: params.ClusterManagerBackoffFactor,
		ClusterManagerSleepInitMS:   params.ClusterManagerSleepInitMS,
		ClusterManagerSleepMaxMS:    params.ClusterManagerSleepMaxMS,
		DataManagerBackoffFactor:    params.DataManagerBackoffFactor,
		DataManagerSleepInitMS:      params.DataManagerSleepInitMS,
		DataManagerSleepMaxMS:       params.DataManagerSleepMaxMS,
		FeedBufferSizeBytes:         params.FeedBufferSizeBytes,
		FeedBufferAckThreshold:      params.FeedBufferAckThreshold,
	}

	feed := &DCPFeed{
		name:       name,
		url:        url,
		poolName:   poolName,
		bucketName: bucketName,
		bucketUUID: bucketUUID,
		params:     params,
		pf:         pf,
		dests:      dests,
		stats:      NewDestStats(),
	}

	feed.bds, err = cbdatasource.NewBucketDataSource(
		strings.Split(url, ";"),
		poolName, bucketName, bucketUUID,
		vbucketIds, auth, feed, options)
	if err != nil {
		return nil, err
	}

	return feed, nil
}

func (t *DCPFeed) Name() string {
	return t.name
}

func (t *DCPFeed) Start() error {
	log.Printf("feed_dcp: start, name: %s", t.Name())
	return t.bds.Start()
}

func (t *DCPFeed) Close() error {
	t.m.Lock()
	if t.closed {
		t.m.Unlock()
		return nil
	}
	t.closed = true
	t.m.Unlock()

	log.Printf("feed_dcp: close, name: %s", t.Name())
	return t.bds.Close()
}

func (t *DCPFeed) Dests() map[string]Dest {
	return t.dests
}

var prefixBucketDataSourceStats = []byte(`{"bucketDataSourceStats":`)
var prefixDestStats = []byte(`,"destStats":`)

func (t *DCPFeed) Stats(w io.Writer) error {
	bdss := cbdatasource.BucketDataSourceStats{}
	err := t.bds.Stats(&bdss)
	if err != nil {
		return err
	}
	w.Write(prefixBucketDataSourceStats)
	json.NewEncoder(w).Encode(&bdss)

	w.Write(prefixDestStats)
	t.stats.WriteJSON(w)

	_, err = w.Write(jsonCloseBrace)
	return err
}

// --------------------------------------------------------

func (r *DCPFeed) OnError(err error) {
	// TODO: Check the type of the error if it's something
	// serious / not-recoverable / needs user attention.
	log.Printf("feed_dcp: on error: %s: %v\n", r.name, err)

	atomic.AddUint64(&r.stats.TotError, 1)

	r.m.Lock()
	r.lastErr = err
	r.m.Unlock()
}

func (r *DCPFeed) DataUpdate(vbucketId uint16, key []byte, seq uint64,
	req *gomemcached.MCRequest) error {
	return Timer(func() error {
		partition, dest, err :=
			VBucketIdToPartitionDest(r.pf, r.dests, vbucketId, key)
		if err != nil {
			return err
		}

		return dest.OnDataUpdate(partition, key, seq, req.Body)
	}, r.stats.TimerOnDataUpdate)
}

func (r *DCPFeed) DataDelete(vbucketId uint16, key []byte, seq uint64,
	req *gomemcached.MCRequest) error {
	return Timer(func() error {
		partition, dest, err :=
			VBucketIdToPartitionDest(r.pf, r.dests, vbucketId, key)
		if err != nil {
			return err
		}

		return dest.OnDataDelete(partition, key, seq)
	}, r.stats.TimerOnDataDelete)
}

func (r *DCPFeed) SnapshotStart(vbucketId uint16,
	snapStart, snapEnd uint64, snapType uint32) error {
	return Timer(func() error {
		partition, dest, err :=
			VBucketIdToPartitionDest(r.pf, r.dests, vbucketId, nil)
		if err != nil {
			return err
		}

		return dest.OnSnapshotStart(partition, snapStart, snapEnd)
	}, r.stats.TimerOnSnapshotStart)
}

func (r *DCPFeed) SetMetaData(vbucketId uint16, value []byte) error {
	return Timer(func() error {
		partition, dest, err :=
			VBucketIdToPartitionDest(r.pf, r.dests, vbucketId, nil)
		if err != nil {
			return err
		}

		return dest.SetOpaque(partition, value)
	}, r.stats.TimerSetOpaque)
}

func (r *DCPFeed) GetMetaData(vbucketId uint16) (
	value []byte, lastSeq uint64, err error) {
	err = Timer(func() error {
		partition, dest, err :=
			VBucketIdToPartitionDest(r.pf, r.dests, vbucketId, nil)
		if err != nil {
			return err
		}

		value, lastSeq, err = dest.GetOpaque(partition)

		return err
	}, r.stats.TimerGetOpaque)

	return value, lastSeq, err
}

func (r *DCPFeed) Rollback(vbucketId uint16, rollbackSeq uint64) error {
	return Timer(func() error {
		log.Printf("feed_dcp: rollback, name: %s: vbucketId: %d,"+
			" rollbackSeq: %d", r.name, vbucketId, rollbackSeq)

		partition, dest, err :=
			VBucketIdToPartitionDest(r.pf, r.dests, vbucketId, nil)
		if err != nil {
			return err
		}

		return dest.Rollback(partition, rollbackSeq)
	}, r.stats.TimerRollback)
}

// -------------------------------------------------------

type vbucketMetaData struct {
	FailOverLog [][]uint64 `json:"failOverLog"`
}

func parseOpaqueToUUID(b []byte) string {
	vmd := &vbucketMetaData{}
	err := json.Unmarshal(b, &vmd)
	if err != nil {
		return ""
	}
	flogLen := len(vmd.FailOverLog)
	if flogLen < 1 || len(vmd.FailOverLog[flogLen-1]) < 1 {
		return ""
	}
	return fmt.Sprintf("%d", vmd.FailOverLog[flogLen-1][0])
}
<commit_msg>log more info on feed_dcp OnError callback<commit_after>//  Copyright (c) 2014 Couchbase, Inc.
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the
//  License. You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//  Unless required by applicable law or agreed to in writing,
//  software distributed under the License is distributed on an "AS
//  IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
//  express or implied. See the License for the specific language
//  governing permissions and limitations under the License.

package cbft

import (
	"encoding/json"
	"fmt"
	"io"
	"math/rand"
	"strings"
	"sync"
	"sync/atomic"

	"github.com/couchbase/gomemcached"
	log "github.com/couchbaselabs/clog"
	"github.com/couchbaselabs/go-couchbase"
	"github.com/couchbaselabs/go-couchbase/cbdatasource"
)

func init() {
	RegisterFeedType("couchbase", &FeedType{
		Start:       StartDCPFeed,
		Partitions:  CouchbasePartitions,
		Public:      true,
		Description: "couchbase - Couchbase Server/Cluster data source",
		StartSample: NewDCPFeedParams(),
	})
	RegisterFeedType("couchbase-dcp", &FeedType{
		Start:       StartDCPFeed,
		Partitions:  CouchbasePartitions,
		Public:      false, // Won't be listed in /api/managerMeta output.
		Description: "couchbase-dcp - Couchbase Server/Cluster data source, via DCP protocol",
		StartSample: NewDCPFeedParams(),
	})
}

func StartDCPFeed(mgr *Manager, feedName, indexName, indexUUID,
	sourceType, bucketName, bucketUUID, params string, dests map[string]Dest) error {
	feed, err := NewDCPFeed(feedName, mgr.server, "default",
		bucketName, bucketUUID, params, BasicPartitionFunc, dests)
	if err != nil {
		return fmt.Errorf("feed_dcp: could not prepare DCP feed to server: %s,"+
			" bucketName: %s, indexName: %s, err: %v",
			mgr.server, bucketName, indexName, err)
	}
	err = feed.Start()
	if err != nil {
		return fmt.Errorf("feed_dcp: could not start dcp feed, server: %s, err: %v",
			mgr.server, err)
	}
	err = mgr.registerFeed(feed)
	if err != nil {
		feed.Close()
		return err
	}
	return nil
}

// A DCPFeed implements both Feed and cbdatasource.Receiver interfaces.
type DCPFeed struct {
	name       string
	url        string
	poolName   string
	bucketName string
	bucketUUID string
	params     *DCPFeedParams
	pf         DestPartitionFunc
	dests      map[string]Dest
	bds        cbdatasource.BucketDataSource

	m       sync.Mutex
	closed  bool
	lastErr error
	stats   *DestStats
}

type DCPFeedParams struct {
	AuthUser     string `json:"authUser"` // May be "" for no auth.
	AuthPassword string `json:"authPassword"`

	// Factor (like 1.5) to increase sleep time between retries
	// in connecting to a cluster manager node.
	ClusterManagerBackoffFactor float32 `json:"clusterManagerBackoffFactor"`

	// Initial sleep time (millisecs) before first retry to cluster manager.
	ClusterManagerSleepInitMS int `json:"clusterManagerSleepInitMS"`

	// Maximum sleep time (millisecs) between retries to cluster manager.
	ClusterManagerSleepMaxMS int `json:"clusterManagerSleepMaxMS"`

	// Factor (like 1.5) to increase sleep time between retries
	// in connecting to a data manager node.
	DataManagerBackoffFactor float32 `json:"dataManagerBackoffFactor"`

	// Initial sleep time (millisecs) before first retry to data manager.
	DataManagerSleepInitMS int `json:"dataManagerSleepInitMS"`

	// Maximum sleep time (millisecs) between retries to data manager.
	DataManagerSleepMaxMS int `json:"dataManagerSleepMaxMS"`

	// Buffer size in bytes provided for UPR flow control.
	FeedBufferSizeBytes uint32 `json:"feedBufferSizeBytes"`

	// Used for UPR flow control and buffer-ack messages when this
	// percentage of FeedBufferSizeBytes is reached.
	FeedBufferAckThreshold float32 `json:"feedBufferAckThreshold"`
}

func NewDCPFeedParams() *DCPFeedParams {
	return &DCPFeedParams{
		ClusterManagerSleepMaxMS: 20000,
		DataManagerSleepMaxMS:    20000,
	}
}

func (d *DCPFeedParams) GetCredentials() (string, string, string) {
	// TODO: bucketName not necessarily userName.
	return d.AuthUser, d.AuthPassword, d.AuthUser
}

func NewDCPFeed(name, url, poolName, bucketName, bucketUUID, paramsStr string,
	pf DestPartitionFunc, dests map[string]Dest) (*DCPFeed, error) {
	params := NewDCPFeedParams()
	if paramsStr != "" {
		err := json.Unmarshal([]byte(paramsStr), params)
		if err != nil {
			return nil, err
		}
	}

	vbucketIds, err := ParsePartitionsToVBucketIds(dests)
	if err != nil {
		return nil, err
	}
	if len(vbucketIds) <= 0 {
		vbucketIds = nil
	}

	var auth couchbase.AuthHandler
	if params.AuthUser != "" {
		auth = params
	}

	options := &cbdatasource.BucketDataSourceOptions{
		Name: fmt.Sprintf("%s-%x", name, rand.Int31()),
		ClusterManagerBackoffFactor: params.ClusterManagerBackoffFactor,
		ClusterManagerSleepInitMS:   params.ClusterManagerSleepInitMS,
		ClusterManagerSleepMaxMS:    params.ClusterManagerSleepMaxMS,
		DataManagerBackoffFactor:    params.DataManagerBackoffFactor,
		DataManagerSleepInitMS:      params.DataManagerSleepInitMS,
		DataManagerSleepMaxMS:       params.DataManagerSleepMaxMS,
		FeedBufferSizeBytes:         params.FeedBufferSizeBytes,
		FeedBufferAckThreshold:      params.FeedBufferAckThreshold,
	}

	feed := &DCPFeed{
		name:       name,
		url:        url,
		poolName:   poolName,
		bucketName: bucketName,
		bucketUUID: bucketUUID,
		params:     params,
		pf:         pf,
		dests:      dests,
		stats:      NewDestStats(),
	}

	feed.bds, err = cbdatasource.NewBucketDataSource(
		strings.Split(url, ";"),
		poolName, bucketName, bucketUUID,
		vbucketIds, auth, feed, options)
	if err != nil {
		return nil, err
	}

	return feed, nil
}

func (t *DCPFeed) Name() string {
	return t.name
}

func (t *DCPFeed) Start() error {
	log.Printf("feed_dcp: start, name: %s", t.Name())
	return t.bds.Start()
}

func (t *DCPFeed) Close() error {
	t.m.Lock()
	if t.closed {
		t.m.Unlock()
		return nil
	}
	t.closed = true
	t.m.Unlock()

	log.Printf("feed_dcp: close, name: %s", t.Name())
	return t.bds.Close()
}

func (t *DCPFeed) Dests() map[string]Dest {
	return t.dests
}

var prefixBucketDataSourceStats = []byte(`{"bucketDataSourceStats":`)
var prefixDestStats = []byte(`,"destStats":`)

func (t *DCPFeed) Stats(w io.Writer) error {
	bdss := cbdatasource.BucketDataSourceStats{}
	err := t.bds.Stats(&bdss)
	if err != nil {
		return err
	}
	w.Write(prefixBucketDataSourceStats)
	json.NewEncoder(w).Encode(&bdss)

	w.Write(prefixDestStats)
	t.stats.WriteJSON(w)

	_, err = w.Write(jsonCloseBrace)
	return err
}

// --------------------------------------------------------

func (r *DCPFeed) OnError(err error) {
	// TODO: Check the type of the error if it's something
	// serious / not-recoverable / needs user attention.
	log.Printf("feed_dcp: OnError, name: %s:"+
		" bucketName: %s, bucketUUID: %s, err: %v\n",
		r.name, r.bucketName, r.bucketUUID, err)

	atomic.AddUint64(&r.stats.TotError, 1)

	r.m.Lock()
	r.lastErr = err
	r.m.Unlock()
}

func (r *DCPFeed) DataUpdate(vbucketId uint16, key []byte, seq uint64,
	req *gomemcached.MCRequest) error {
	return Timer(func() error {
		partition, dest, err :=
			VBucketIdToPartitionDest(r.pf, r.dests, vbucketId, key)
		if err != nil {
			return err
		}

		return dest.OnDataUpdate(partition, key, seq, req.Body)
	}, r.stats.TimerOnDataUpdate)
}

func (r *DCPFeed) DataDelete(vbucketId uint16, key []byte, seq uint64,
	req *gomemcached.MCRequest) error {
	return Timer(func() error {
		partition, dest, err :=
			VBucketIdToPartitionDest(r.pf, r.dests, vbucketId, key)
		if err != nil {
			return err
		}

		return dest.OnDataDelete(partition, key, seq)
	}, r.stats.TimerOnDataDelete)
}

func (r *DCPFeed) SnapshotStart(vbucketId uint16,
	snapStart, snapEnd uint64, snapType uint32) error {
	return Timer(func() error {
		partition, dest, err :=
			VBucketIdToPartitionDest(r.pf, r.dests, vbucketId, nil)
		if err != nil {
			return err
		}

		return dest.OnSnapshotStart(partition, snapStart, snapEnd)
	}, r.stats.TimerOnSnapshotStart)
}

func (r *DCPFeed) SetMetaData(vbucketId uint16, value []byte) error {
	return Timer(func() error {
		partition, dest, err :=
			VBucketIdToPartitionDest(r.pf, r.dests, vbucketId, nil)
		if err != nil {
			return err
		}

		return dest.SetOpaque(partition, value)
	}, r.stats.TimerSetOpaque)
}

func (r *DCPFeed) GetMetaData(vbucketId uint16) (
	value []byte, lastSeq uint64, err error) {
	err = Timer(func() error {
		partition, dest, err :=
			VBucketIdToPartitionDest(r.pf, r.dests, vbucketId, nil)
		if err != nil {
			return err
		}

		value, lastSeq, err = dest.GetOpaque(partition)

		return err
	}, r.stats.TimerGetOpaque)

	return value, lastSeq, err
}

func (r *DCPFeed) Rollback(vbucketId uint16, rollbackSeq uint64) error {
	return Timer(func() error {
		log.Printf("feed_dcp: rollback, name: %s: vbucketId: %d,"+
			" rollbackSeq: %d", r.name, vbucketId, rollbackSeq)

		partition, dest, err :=
			VBucketIdToPartitionDest(r.pf, r.dests, vbucketId, nil)
		if err != nil {
			return err
		}

		return dest.Rollback(partition, rollbackSeq)
	}, r.stats.TimerRollback)
}

// -------------------------------------------------------

type vbucketMetaData struct {
	FailOverLog [][]uint64 `json:"failOverLog"`
}

func parseOpaqueToUUID(b []byte) string {
	vmd := &vbucketMetaData{}
	err := json.Unmarshal(b, &vmd)
	if err != nil {
		return ""
	}
	flogLen := len(vmd.FailOverLog)
	if flogLen < 1 || len(vmd.FailOverLog[flogLen-1]) < 1 {
		return ""
	}
	return fmt.Sprintf("%d", vmd.FailOverLog[flogLen-1][0])
}
<|endoftext|>