Huawei Cloud Provider implementation (#241)
* add huawei CCI provider * add readme * add vender * add huawei provider mock test
This commit is contained in:
1
AUTHORS
1
AUTHORS
@@ -8,3 +8,4 @@ Ria Bhatia <ria.bhatia@microsoft.com>
|
||||
Rita Zhang <rita.z.zhang@gmail.com>
|
||||
Robbie Zhang <junjiez@microsoft.com>
|
||||
Ben Corrie <bcorrie@vmware.com>
|
||||
Fei Xu <xufei40@huawei.com>
|
||||
|
||||
85
providers/huawei/README.md
Normal file
85
providers/huawei/README.md
Normal file
@@ -0,0 +1,85 @@
|
||||
# Huawei CCI
|
||||
|
||||
Huawei CCI [(Cloud Container Instance)](https://www.huaweicloud.com/product/cci.html) service provides serverless container management,
|
||||
and does not require users to manage the cluster and the server.
|
||||
Only through simple configuration, users can enjoy the agility and high performance of the container.
|
||||
CCI supports stateless workloads (Deployment) and stateful workload (StatefulSet).
|
||||
On the basis of Kubernetes, we have made a series of important enhancements such as secure container,
|
||||
elastic load balancing, elastic scalability, Blue Green Deployment and so on.
|
||||
|
||||
## Huawei CCI Virtual Kubelet Provider
|
||||
|
||||
Huawei CCI virtual kubelet provider configures a CCI project as node in any of your Kubernetes cluster,
|
||||
such as Huawei CCE [(Cloud Container Engine)](https://www.huaweicloud.com/en-us/product/cce.html).
|
||||
CCE supports native Kubernetes applications and tools as private cluster, allowing you to easily set up a container runtime environment.
|
||||
Pod which is scheduled to the virtual kubelet provider will run in the CCI, that will makes good use of the high performance of CCI.
|
||||
The diagram below illustrates how Huawei CCI virtual kubelet provider works.
|
||||
|
||||

|
||||
|
||||
**NOTE:** The Huawei CCI virtual-kubelet provider is in the early stages of development,
|
||||
and don't use it in a production environment.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
You must install the provider in a Kubernetes cluster and connect to the CCI, and also need create an account for CCI.
|
||||
Once you've created your account, then need to record the aksk, region for the configuration in next step.
|
||||
|
||||
## Configuration
|
||||
|
||||
Before run CCI Virtual Kubelet Provider, you must do as the following steps.
|
||||
1. Create a configuration profile.
|
||||
You need to provide the fields you specify like in the [example fils](cci.toml).
|
||||
2. Copy your AKSK and save them in environment variable:
|
||||
```console
|
||||
export APP_KEY="<AppKey>"
|
||||
export APP_SECRET="<AppSecret>"
|
||||
```
|
||||
|
||||
## Connect to CCI from your cluster via Virtual Kubelet
|
||||
|
||||
On the Kubernetes work node, starting a virtual-kubelet process as follows.
|
||||
|
||||
```console
|
||||
virtual-kubelet --provider huawei --provider-config cci.toml
|
||||
```
|
||||
Then run ``kubectl get nodes`` in your cluster to validate the provider has been running as a node.
|
||||
|
||||
```console
|
||||
kubectl get nodes
|
||||
NAME STATUS AGE
|
||||
virtual-kubelet Ready 5m
|
||||
cce-192.168.0.178 Ready 10d
|
||||
cce-192.168.0.233 Ready 10d
|
||||
```
|
||||
If want to stop the virtual kubelet, just stop the virtual kubelet process.
|
||||
|
||||
## Schedule pod to CCI via Virtual Kubelet
|
||||
|
||||
```console
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: myapp
|
||||
labels:
|
||||
app: myapp
|
||||
spec:
|
||||
nodeName: virtual-kubelet
|
||||
containers:
|
||||
- name: nginx
|
||||
image: 1and1internet/ubuntu-16-nginx
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
tolerations:
|
||||
- key: huawei.com/cci
|
||||
effect: NoSchedule
|
||||
```
|
||||
Replace the nodeName to the virtual-kubelet nodename and save the configuration to a file ``virtual-kubelet-pod.yaml``.
|
||||
Then run ``kubectl create -f virtual-kubelet-pod.yaml`` to create the pod. Run ``kubectl get pods -owide`` to get pods.
|
||||
|
||||
```console
|
||||
kubectl get pods -o wide
|
||||
NAME READY STATUS RESTARTS AGE IP NODE
|
||||
myapp-7c7877989-vbffm 1/1 Running 0 39s 172.17.0.3 virtual-kubelet
|
||||
```
|
||||
309
providers/huawei/auth/auth.go
Normal file
309
providers/huawei/auth/auth.go
Normal file
@@ -0,0 +1,309 @@
|
||||
package auth
|
||||
|
||||
// HWS API Gateway Signature
|
||||
// Analog to AWS Signature Version 4, with some HWS specific parameters
|
||||
// Please refer to: http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// BasicDateFormat and BasicDateFormatShort define aws-date format
|
||||
const (
|
||||
BasicDateFormat = "20060102T150405Z"
|
||||
BasicDateFormatShort = "20060102"
|
||||
TerminationString = "sdk_request"
|
||||
Algorithm = "SDK-HMAC-SHA256"
|
||||
PreSKString = "SDK"
|
||||
HeaderXDate = "x-sdk-date"
|
||||
HeaderDate = "date"
|
||||
HeaderHost = "host"
|
||||
HeaderAuthorization = "Authorization"
|
||||
HeaderContentSha256 = "x-sdk-content-sha256"
|
||||
// todo: use the region and service.
|
||||
DefaultRegion = "default"
|
||||
DefaultService = "apigateway"
|
||||
)
|
||||
|
||||
func shouldEscape(c byte) bool {
|
||||
if 'A' <= c && c <= 'Z' || 'a' <= c && c <= 'z' || '0' <= c && c <= '9' || c == '_' || c == '-' || c == '~' || c == '.' {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
func escape(s string) string {
|
||||
hexCount := 0
|
||||
for i := 0; i < len(s); i++ {
|
||||
c := s[i]
|
||||
if shouldEscape(c) {
|
||||
hexCount++
|
||||
}
|
||||
}
|
||||
|
||||
if hexCount == 0 {
|
||||
return s
|
||||
}
|
||||
|
||||
t := make([]byte, len(s)+2*hexCount)
|
||||
j := 0
|
||||
for i := 0; i < len(s); i++ {
|
||||
switch c := s[i]; {
|
||||
case shouldEscape(c):
|
||||
t[j] = '%'
|
||||
t[j+1] = "0123456789ABCDEF"[c>>4]
|
||||
t[j+2] = "0123456789ABCDEF"[c&15]
|
||||
j += 3
|
||||
default:
|
||||
t[j] = s[i]
|
||||
j++
|
||||
}
|
||||
}
|
||||
return string(t)
|
||||
}
|
||||
|
||||
func hmacsha256(key []byte, data string) ([]byte, error) {
|
||||
h := hmac.New(sha256.New, []byte(key))
|
||||
if _, err := h.Write([]byte(data)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return h.Sum(nil), nil
|
||||
}
|
||||
|
||||
// Build a CanonicalRequest from a regular request string
|
||||
//
|
||||
// See http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
|
||||
// CanonicalRequest =
|
||||
// HTTPRequestMethod + '\n' +
|
||||
// CanonicalURI + '\n' +
|
||||
// CanonicalQueryString + '\n' +
|
||||
// CanonicalHeaders + '\n' +
|
||||
// SignedHeaders + '\n' +
|
||||
// HexEncode(Hash(RequestPayload))
|
||||
func CanonicalRequest(r *http.Request) (string, error) {
|
||||
var hexencode string
|
||||
var err error
|
||||
if hex := r.Header.Get(HeaderContentSha256); hex != "" {
|
||||
hexencode = hex
|
||||
} else {
|
||||
data, err := RequestPayload(r)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
hexencode, err = HexEncodeSHA256Hash(data)
|
||||
}
|
||||
return fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s", r.Method, CanonicalURI(r), CanonicalQueryString(r), CanonicalHeaders(r), SignedHeaders(r), hexencode), err
|
||||
}
|
||||
|
||||
// CanonicalURI returns request uri
|
||||
func CanonicalURI(r *http.Request) string {
|
||||
pattens := strings.Split(r.URL.Path, "/")
|
||||
var uri []string
|
||||
for _, v := range pattens {
|
||||
switch v {
|
||||
case "":
|
||||
continue
|
||||
case ".":
|
||||
continue
|
||||
case "..":
|
||||
if len(uri) > 0 {
|
||||
uri = uri[:len(uri)-1]
|
||||
}
|
||||
default:
|
||||
uri = append(uri, escape(v))
|
||||
}
|
||||
}
|
||||
urlpath := "/"
|
||||
if len(uri) > 0 {
|
||||
urlpath = urlpath + strings.Join(uri, "/") + "/"
|
||||
}
|
||||
return urlpath
|
||||
}
|
||||
|
||||
// CanonicalQueryString
|
||||
func CanonicalQueryString(r *http.Request) string {
|
||||
var a []string
|
||||
for key, value := range r.URL.Query() {
|
||||
k := escape(key)
|
||||
for _, v := range value {
|
||||
var kv string
|
||||
if v == "" {
|
||||
kv = k
|
||||
} else {
|
||||
kv = fmt.Sprintf("%s=%s", k, escape(v))
|
||||
}
|
||||
a = append(a, kv)
|
||||
}
|
||||
}
|
||||
sort.Strings(a)
|
||||
query := strings.Join(a, "&")
|
||||
r.URL.RawQuery = query
|
||||
return query
|
||||
}
|
||||
|
||||
// CanonicalHeaders
|
||||
func CanonicalHeaders(r *http.Request) string {
|
||||
var a []string
|
||||
for key, value := range r.Header {
|
||||
sort.Strings(value)
|
||||
var q []string
|
||||
for _, v := range value {
|
||||
q = append(q, trimString(v))
|
||||
}
|
||||
a = append(a, strings.ToLower(key)+":"+strings.Join(q, ","))
|
||||
}
|
||||
a = append(a, HeaderHost+":"+r.Host)
|
||||
sort.Strings(a)
|
||||
return fmt.Sprintf("%s\n", strings.Join(a, "\n"))
|
||||
}
|
||||
|
||||
// SignedHeaders
|
||||
func SignedHeaders(r *http.Request) string {
|
||||
var a []string
|
||||
for key := range r.Header {
|
||||
a = append(a, strings.ToLower(key))
|
||||
}
|
||||
a = append(a, HeaderHost)
|
||||
sort.Strings(a)
|
||||
return fmt.Sprintf("%s", strings.Join(a, ";"))
|
||||
}
|
||||
|
||||
// RequestPayload
|
||||
func RequestPayload(r *http.Request) ([]byte, error) {
|
||||
if r.Body == nil {
|
||||
return []byte(""), nil
|
||||
}
|
||||
b, err := ioutil.ReadAll(r.Body)
|
||||
r.Body = ioutil.NopCloser(bytes.NewBuffer(b))
|
||||
return b, err
|
||||
}
|
||||
|
||||
// Return the Credential Scope. See http://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html
|
||||
func CredentialScope(t time.Time, regionName, serviceName string) string {
|
||||
return fmt.Sprintf("%s/%s/%s/%s", t.UTC().Format(BasicDateFormatShort), regionName, serviceName, TerminationString)
|
||||
}
|
||||
|
||||
// Create a "String to Sign". See http://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html
|
||||
func StringToSign(canonicalRequest, credentialScope string, t time.Time) string {
|
||||
hash := sha256.New()
|
||||
hash.Write([]byte(canonicalRequest))
|
||||
return fmt.Sprintf("%s\n%s\n%s\n%x",
|
||||
Algorithm, t.UTC().Format(BasicDateFormat), credentialScope, hash.Sum(nil))
|
||||
}
|
||||
|
||||
// Generate a "signing key" to sign the "String To Sign". See http://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html
|
||||
func GenerateSigningKey(secretKey, regionName, serviceName string, t time.Time) ([]byte, error) {
|
||||
|
||||
key := []byte(PreSKString + secretKey)
|
||||
var err error
|
||||
dateStamp := t.UTC().Format(BasicDateFormatShort)
|
||||
data := []string{dateStamp, regionName, serviceName, TerminationString}
|
||||
for _, d := range data {
|
||||
key, err = hmacsha256(key, d)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// Create the HWS Signature. See http://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html
|
||||
func SignStringToSign(stringToSign string, signingKey []byte) (string, error) {
|
||||
hm, err := hmacsha256(signingKey, stringToSign)
|
||||
return fmt.Sprintf("%x", hm), err
|
||||
}
|
||||
|
||||
// HexEncodeSHA256Hash returns hexcode of sha256
|
||||
func HexEncodeSHA256Hash(body []byte) (string, error) {
|
||||
hash := sha256.New()
|
||||
if body == nil {
|
||||
body = []byte("")
|
||||
}
|
||||
_, err := hash.Write(body)
|
||||
return fmt.Sprintf("%x", hash.Sum(nil)), err
|
||||
}
|
||||
|
||||
// Get the finalized value for the "Authorization" header. The signature parameter is the output from SignStringToSign
|
||||
func AuthHeaderValue(signature, accessKey, credentialScope, signedHeaders string) string {
|
||||
return fmt.Sprintf("%s Credential=%s/%s, SignedHeaders=%s, Signature=%s", Algorithm, accessKey, credentialScope, signedHeaders, signature)
|
||||
}
|
||||
|
||||
func trimString(s string) string {
|
||||
var trimedString []byte
|
||||
inQuote := false
|
||||
var lastChar byte
|
||||
s = strings.TrimSpace(s)
|
||||
for _, v := range []byte(s) {
|
||||
if byte(v) == byte('"') {
|
||||
inQuote = !inQuote
|
||||
}
|
||||
if lastChar == byte(' ') && byte(v) == byte(' ') && !inQuote {
|
||||
continue
|
||||
}
|
||||
trimedString = append(trimedString, v)
|
||||
lastChar = v
|
||||
}
|
||||
return string(trimedString)
|
||||
}
|
||||
|
||||
type Signer interface {
|
||||
Sign(*http.Request) error
|
||||
}
|
||||
|
||||
// Signature HWS meta
|
||||
type SignerHws struct {
|
||||
AppKey string
|
||||
AppSecret string
|
||||
Region string
|
||||
Service string
|
||||
}
|
||||
|
||||
// SignRequest set Authorization header
|
||||
func (s *SignerHws) Sign(r *http.Request) error {
|
||||
var t time.Time
|
||||
var err error
|
||||
var dt string
|
||||
if dt = r.Header.Get(HeaderXDate); dt != "" {
|
||||
t, err = time.Parse(BasicDateFormat, dt)
|
||||
} else if dt = r.Header.Get(HeaderDate); dt != "" {
|
||||
t, err = time.Parse(time.RFC1123, dt)
|
||||
}
|
||||
if err != nil || dt == "" {
|
||||
r.Header.Del(HeaderDate)
|
||||
t = time.Now()
|
||||
r.Header.Set(HeaderXDate, t.UTC().Format(BasicDateFormat))
|
||||
}
|
||||
canonicalRequest, err := CanonicalRequest(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
Region := DefaultRegion
|
||||
Service := DefaultService
|
||||
if s.Region != "" {
|
||||
Region = s.Region
|
||||
}
|
||||
if s.Service != "" {
|
||||
Service = s.Service
|
||||
}
|
||||
credentialScope := CredentialScope(t, Region, Service)
|
||||
stringToSign := StringToSign(canonicalRequest, credentialScope, t)
|
||||
key, err := GenerateSigningKey(s.AppSecret, Region, Service, t)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
signature, err := SignStringToSign(stringToSign, key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
signedHeaders := SignedHeaders(r)
|
||||
authValue := AuthHeaderValue(signature, s.AppKey, credentialScope, signedHeaders)
|
||||
r.Header.Set(HeaderAuthorization, authValue)
|
||||
return nil
|
||||
}
|
||||
784
providers/huawei/cci-provider.svg
Normal file
784
providers/huawei/cci-provider.svg
Normal file
@@ -0,0 +1,784 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- 由 Microsoft Visio, SVG Export 生成 cci-provider.svg Page-1 -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
|
||||
xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="8.02082in" height="4.62889in"
|
||||
viewBox="0 0 577.499 333.28" xml:space="preserve" color-interpolation-filters="sRGB" class="st19">
|
||||
<v:documentProperties v:langID="2052" v:metric="true" v:viewMarkup="false">
|
||||
<v:userDefs>
|
||||
<v:ud v:nameU="msvSubprocessMaster" v:prompt="" v:val="VT4(Rectangle)"/>
|
||||
<v:ud v:nameU="msvNoAutoConnect" v:val="VT0(1):26"/>
|
||||
</v:userDefs>
|
||||
</v:documentProperties>
|
||||
|
||||
<style type="text/css">
|
||||
<![CDATA[
|
||||
.st1 {fill:#ffffff;stroke:#41719c;stroke-width:0.75}
|
||||
.st2 {visibility:visible}
|
||||
.st3 {fill:#5b9bd5;fill-opacity:0.22;filter:url(#filter_2);stroke:#5b9bd5;stroke-opacity:0.22}
|
||||
.st4 {fill:#ffffff;stroke:#5b9bd5;stroke-width:0.25}
|
||||
.st5 {fill:#e2efd9;stroke:#61973d;stroke-dasharray:5.25,3.75;stroke-width:0.75}
|
||||
.st6 {fill:none;stroke:none;stroke-width:0.25}
|
||||
.st7 {fill:#000000;font-family:Calibri;font-size:1.33333em}
|
||||
.st8 {fill:#000000;font-family:Calibri;font-size:1.00001em}
|
||||
.st9 {fill:#c5e0b3;stroke:#c7c8c8;stroke-width:0.25}
|
||||
.st10 {fill:#000000;font-family:Calibri;font-size:1.00001em;font-weight:bold}
|
||||
.st11 {marker-end:url(#mrkr1-32);stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:1}
|
||||
.st12 {fill:#000000;fill-opacity:1;stroke:#000000;stroke-opacity:1;stroke-width:0.28409090909091}
|
||||
.st13 {fill:#9cc3e5;stroke:#c7c8c8;stroke-width:0.25}
|
||||
.st14 {fill:#000000;font-family:Calibri;font-size:1.16666em;font-weight:bold}
|
||||
.st15 {fill:#000000;font-family:Calibri;font-size:1.33333em;font-weight:bold}
|
||||
.st16 {fill:#ffffff;stroke:#41719c;stroke-dasharray:3.5,2.5;stroke-width:0.5}
|
||||
.st17 {fill:#e2efd9;stroke:#61973d;stroke-width:0.75}
|
||||
.st18 {fill:#000000;font-family:Calibri;font-size:1.16666em}
|
||||
.st19 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
|
||||
]]>
|
||||
</style>
|
||||
|
||||
<defs id="Markers">
|
||||
<g id="lend1">
|
||||
<path d="M 1 -1 L 0 0 L 1 1 " style="stroke-linecap:round;stroke-linejoin:round;fill:none"/>
|
||||
</g>
|
||||
<marker id="mrkr1-32" class="st12" v:arrowType="1" v:arrowSize="2" orient="auto" markerUnits="strokeWidth"
|
||||
overflow="visible">
|
||||
<use xlink:href="#lend1" transform="scale(-3.52,-3.52) "/>
|
||||
</marker>
|
||||
</defs>
|
||||
<defs id="Filters">
|
||||
<filter id="filter_2">
|
||||
<feGaussianBlur stdDeviation="2"/>
|
||||
</filter>
|
||||
</defs>
|
||||
<g v:mID="0" v:index="1" v:groupContext="foregroundPage">
|
||||
<v:userDefs>
|
||||
<v:ud v:nameU="msvThemeOrder" v:val="VT0(0):26"/>
|
||||
</v:userDefs>
|
||||
<title>页-1</title>
|
||||
<v:pageProperties v:drawingScale="0.0393701" v:pageScale="0.0393701" v:drawingUnits="24" v:shadowOffsetX="8.50394"
|
||||
v:shadowOffsetY="-8.50394"/>
|
||||
<g id="shape17-1" v:mID="17" v:groupContext="shape" transform="translate(18.7496,-157.931)">
|
||||
<title>圆角的矩形.17</title>
|
||||
<v:userDefs>
|
||||
<v:ud v:nameU="CTypeTopLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeTopRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockHoriz" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockVert" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockDiag" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.34251968503937):1"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.34251968503937):1"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.34251968503937):1"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.34251968503937):1"/>
|
||||
</v:userDefs>
|
||||
<path d="M24.66 333.28 L221.95 333.28 A24.661 24.661 -180 0 0 246.61 308.62 L246.61 206.51 A24.661 24.661 -180 0 0 221.95
|
||||
181.85 L24.66 181.85 A24.661 24.661 -180 0 0 -0 206.51 L0 308.62 A24.661 24.661 -180 0 0 24.66 333.28 Z"
|
||||
class="st1"/>
|
||||
</g>
|
||||
<g id="shape85-3" v:mID="85" v:groupContext="shape" transform="translate(33.1466,-173.969)">
|
||||
<title>圆角的矩形</title>
|
||||
<v:userDefs>
|
||||
<v:ud v:nameU="CTypeTopLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeTopRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockHoriz" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockVert" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockDiag" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
|
||||
</v:userDefs>
|
||||
<g id="shadow85-4" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
|
||||
transform="matrix(1,0,0,1,0.345598,1.97279)" class="st2">
|
||||
<path d="M11.34 333.28 L102.05 333.28 A11.3384 11.3384 -180 0 0 113.39 321.94 L113.39 287.93 A11.3384 11.3384 -180
|
||||
0 0 102.05 276.59 L11.34 276.59 A11.3384 11.3384 -180 0 0 0 287.93 L0 321.94 A11.3384 11.3384 -180 0
|
||||
0 11.34 333.28 Z" class="st3"/>
|
||||
</g>
|
||||
<path d="M11.34 333.28 L102.05 333.28 A11.3384 11.3384 -180 0 0 113.39 321.94 L113.39 287.93 A11.3384 11.3384 -180 0
|
||||
0 102.05 276.59 L11.34 276.59 A11.3384 11.3384 -180 0 0 0 287.93 L0 321.94 A11.3384 11.3384 -180 0 0 11.34
|
||||
333.28 Z" class="st4"/>
|
||||
</g>
|
||||
<g id="shape5-8" v:mID="5" v:groupContext="shape" transform="translate(89.6157,-242.747)">
|
||||
<title>圆角的矩形.5</title>
|
||||
<v:userDefs>
|
||||
<v:ud v:nameU="CTypeTopLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeTopRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockHoriz" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockVert" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockDiag" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.11811023622047):1"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.11811023622047):1"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.11811023622047):1"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.11811023622047):1"/>
|
||||
</v:userDefs>
|
||||
<path d="M8.5 333.28 L76.54 333.28 A8.5038 8.5038 -180 0 0 85.04 324.78 L85.04 295.01 A8.5038 8.5038 -180 0 0 76.54 286.51
|
||||
L8.5 286.51 A8.5038 8.5038 -180 0 0 -0 295.01 L0 324.78 A8.5038 8.5038 -180 0 0 8.5 333.28 Z" class="st5"/>
|
||||
</g>
|
||||
<g id="shape7-10" v:mID="7" v:groupContext="shape" transform="translate(140.639,-281.014)">
|
||||
<title>工作表.7</title>
|
||||
<desc>CCE-Cluster1</desc>
|
||||
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
|
||||
<v:textRect cx="62.3622" cy="316.272" width="124.73" height="34.0157"/>
|
||||
<rect x="0" y="299.264" width="124.724" height="34.0157" class="st6"/>
|
||||
<text x="20.54" y="321.07" class="st7" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>CCE-Cluster1</text> </g>
|
||||
<g id="shape8-13" v:mID="8" v:groupContext="shape" transform="translate(100.954,-255.503)">
|
||||
<title>工作表.8</title>
|
||||
<desc>POD</desc>
|
||||
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
|
||||
<v:textRect cx="28.3465" cy="316.272" width="56.7" height="34.0157"/>
|
||||
<rect x="0" y="299.264" width="56.6929" height="34.0157" class="st6"/>
|
||||
<text x="17.58" y="319.87" class="st8" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>POD</text> </g>
|
||||
<g id="shape9-16" v:mID="9" v:groupContext="shape" transform="translate(75.4425,-238.495)">
|
||||
<title>工作表.9</title>
|
||||
<desc>nodeName: VK</desc>
|
||||
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
|
||||
<v:textRect cx="55.2756" cy="316.272" width="110.56" height="34.0157"/>
|
||||
<rect x="0" y="299.264" width="110.551" height="34.0157" class="st6"/>
|
||||
<text x="18.81" y="319.87" class="st8" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>nodeName: VK</text> </g>
|
||||
<g id="shape11-19" v:mID="11" v:groupContext="shape" transform="translate(159.065,-184.636)">
|
||||
<title>圆角的矩形.11</title>
|
||||
<v:userDefs>
|
||||
<v:ud v:nameU="CTypeTopLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeTopRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockHoriz" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockVert" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockDiag" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.13779527559055):1"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.13779527559055):1"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.13779527559055):1"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.13779527559055):1"/>
|
||||
</v:userDefs>
|
||||
<g id="shadow11-20" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
|
||||
transform="matrix(1,0,0,1,0.345598,1.97279)" class="st2">
|
||||
<path d="M9.92 333.28 L89.29 333.28 A9.9211 9.9211 -180 0 0 99.21 323.36 L99.21 303.52 A9.9211 9.9211 -180 0 0 89.29
|
||||
293.6 L9.92 293.6 A9.9211 9.9211 -180 0 0 0 303.52 L0 323.36 A9.9211 9.9211 -180 0 0 9.92 333.28 Z"
|
||||
class="st3"/>
|
||||
</g>
|
||||
<path d="M9.92 333.28 L89.29 333.28 A9.9211 9.9211 -180 0 0 99.21 323.36 L99.21 303.52 A9.9211 9.9211 -180 0 0 89.29
|
||||
293.6 L9.92 293.6 A9.9211 9.9211 -180 0 0 0 303.52 L0 323.36 A9.9211 9.9211 -180 0 0 9.92 333.28 Z"
|
||||
class="st9"/>
|
||||
</g>
|
||||
<g id="shape12-24" v:mID="12" v:groupContext="shape" transform="translate(167.568,-187.471)">
|
||||
<title>工作表.12</title>
|
||||
<desc>Virtual-Kubelet</desc>
|
||||
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
|
||||
<v:textRect cx="42.5197" cy="316.272" width="85.04" height="34.0157"/>
|
||||
<rect x="0" y="299.264" width="85.0394" height="34.0157" class="st6"/>
|
||||
<text x="4.48" y="319.87" class="st10" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Virtual-Kubelet</text> </g>
|
||||
<g id="shape14-27" v:mID="14" v:groupContext="shape" transform="translate(433.184,-143.181) rotate(50.8696)">
|
||||
<title>工作表.14</title>
|
||||
<path d="M0 333.28 L53.9 333.28" class="st11"/>
|
||||
</g>
|
||||
<g id="shape22-33" v:mID="22" v:groupContext="shape" transform="translate(159.065,-184.636)">
|
||||
<title>圆角的矩形.22</title>
|
||||
<v:userDefs>
|
||||
<v:ud v:nameU="CTypeTopLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeTopRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockHoriz" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockVert" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockDiag" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.13779527559055):1"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.13779527559055):1"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.13779527559055):1"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.13779527559055):1"/>
|
||||
</v:userDefs>
|
||||
<g id="shadow22-34" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
|
||||
transform="matrix(1,0,0,1,0.345598,1.97279)" class="st2">
|
||||
<path d="M9.92 333.28 L89.29 333.28 A9.9211 9.9211 -180 0 0 99.21 323.36 L99.21 303.52 A9.9211 9.9211 -180 0 0 89.29
|
||||
293.6 L9.92 293.6 A9.9211 9.9211 -180 0 0 0 303.52 L0 323.36 A9.9211 9.9211 -180 0 0 9.92 333.28 Z"
|
||||
class="st3"/>
|
||||
</g>
|
||||
<path d="M9.92 333.28 L89.29 333.28 A9.9211 9.9211 -180 0 0 99.21 323.36 L99.21 303.52 A9.9211 9.9211 -180 0 0 89.29
|
||||
293.6 L9.92 293.6 A9.9211 9.9211 -180 0 0 0 303.52 L0 323.36 A9.9211 9.9211 -180 0 0 9.92 333.28 Z"
|
||||
class="st9"/>
|
||||
</g>
|
||||
<g id="shape23-38" v:mID="23" v:groupContext="shape" transform="translate(167.568,-187.471)">
|
||||
<title>工作表.23</title>
|
||||
<desc>Virtual-Kubelet</desc>
|
||||
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
|
||||
<v:textRect cx="42.5197" cy="316.272" width="85.04" height="34.0157"/>
|
||||
<rect x="0" y="299.264" width="85.0394" height="34.0157" class="st6"/>
|
||||
<text x="4.48" y="319.87" class="st10" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Virtual-Kubelet</text> </g>
|
||||
<g id="shape37-41" v:mID="37" v:groupContext="shape" transform="translate(93.9143,-192.618)">
|
||||
<title>圆角的矩形.37</title>
|
||||
<v:userDefs>
|
||||
<v:ud v:nameU="CTypeTopLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeTopRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockHoriz" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockVert" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockDiag" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.061023622047244):1"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.061023622047244):1"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.061023622047244):1"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.061023622047244):1"/>
|
||||
</v:userDefs>
|
||||
<g id="shadow37-42" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
|
||||
transform="matrix(1,0,0,1,0.345598,1.97279)" class="st2">
|
||||
<path d="M4.39 333.28 L39.54 333.28 A4.39363 4.39363 -180 0 0 43.94 328.89 L43.94 309.33 A4.39363 4.39363 -180 0
|
||||
0 39.54 304.93 L4.39 304.93 A4.39363 4.39363 -180 0 0 0 309.33 L0 328.89 A4.39363 4.39363 -180 0 0 4.39
|
||||
333.28 Z" class="st3"/>
|
||||
</g>
|
||||
<path d="M4.39 333.28 L39.54 333.28 A4.39363 4.39363 -180 0 0 43.94 328.89 L43.94 309.33 A4.39363 4.39363 -180 0 0 39.54
|
||||
304.93 L4.39 304.93 A4.39363 4.39363 -180 0 0 0 309.33 L0 328.89 A4.39363 4.39363 -180 0 0 4.39 333.28 Z"
|
||||
class="st13"/>
|
||||
</g>
|
||||
<g id="shape38-46" v:mID="38" v:groupContext="shape" transform="translate(98.1663,-194.602)">
|
||||
<title>工作表.38</title>
|
||||
<desc>POD</desc>
|
||||
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
|
||||
<v:textRect cx="18.4252" cy="320.808" width="36.86" height="24.9449"/>
|
||||
<rect x="0" y="308.335" width="36.8504" height="24.9449" class="st6"/>
|
||||
<text x="7.66" y="324.41" class="st8" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>POD</text> </g>
|
||||
<g id="shape39-49" v:mID="39" v:groupContext="shape" transform="translate(42.5364,-192.618)">
|
||||
<title>圆角的矩形.39</title>
|
||||
<v:userDefs>
|
||||
<v:ud v:nameU="CTypeTopLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeTopRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockHoriz" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockVert" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockDiag" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.061023622047244):1"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.061023622047244):1"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.061023622047244):1"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.061023622047244):1"/>
|
||||
</v:userDefs>
|
||||
<g id="shadow39-50" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
|
||||
transform="matrix(1,0,0,1,0.345598,1.97279)" class="st2">
|
||||
<path d="M4.39 333.28 L39.54 333.28 A4.39363 4.39363 -180 0 0 43.94 328.89 L43.94 309.33 A4.39363 4.39363 -180 0
|
||||
0 39.54 304.93 L4.39 304.93 A4.39363 4.39363 -180 0 0 0 309.33 L0 328.89 A4.39363 4.39363 -180 0 0 4.39
|
||||
333.28 Z" class="st3"/>
|
||||
</g>
|
||||
<path d="M4.39 333.28 L39.54 333.28 A4.39363 4.39363 -180 0 0 43.94 328.89 L43.94 309.33 A4.39363 4.39363 -180 0 0 39.54
|
||||
304.93 L4.39 304.93 A4.39363 4.39363 -180 0 0 0 309.33 L0 328.89 A4.39363 4.39363 -180 0 0 4.39 333.28 Z"
|
||||
class="st13"/>
|
||||
</g>
|
||||
<g id="shape40-54" v:mID="40" v:groupContext="shape" transform="translate(44.6624,-193.185)">
|
||||
<title>工作表.40</title>
|
||||
<desc>POD</desc>
|
||||
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
|
||||
<v:textRect cx="18.4252" cy="320.808" width="36.86" height="24.9449"/>
|
||||
<rect x="0" y="308.335" width="36.8504" height="24.9449" class="st6"/>
|
||||
<text x="7.66" y="324.41" class="st8" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>POD</text> </g>
|
||||
<g id="shape42-57" v:mID="42" v:groupContext="shape" transform="translate(363.159,-240.509)">
|
||||
<title>圆角的矩形.42</title>
|
||||
<v:userDefs>
|
||||
<v:ud v:nameU="CTypeTopLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeTopRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockHoriz" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockVert" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockDiag" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.11811023622047):1"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.11811023622047):1"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.11811023622047):1"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.11811023622047):1"/>
|
||||
</v:userDefs>
|
||||
<path d="M8.5 333.28 L76.54 333.28 A8.5038 8.5038 -180 0 0 85.04 324.78 L85.04 295.01 A8.5038 8.5038 -180 0 0 76.54 286.51
|
||||
L8.5 286.51 A8.5038 8.5038 -180 0 0 -0 295.01 L0 324.78 A8.5038 8.5038 -180 0 0 8.5 333.28 Z" class="st5"/>
|
||||
</g>
|
||||
<g id="shape43-59" v:mID="43" v:groupContext="shape" transform="translate(312.135,-279.131)">
|
||||
<title>工作表.43</title>
|
||||
<desc>CCE-Cluster1</desc>
|
||||
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
|
||||
<v:textRect cx="62.3622" cy="316.272" width="124.73" height="34.0157"/>
|
||||
<rect x="0" y="299.264" width="124.724" height="34.0157" class="st6"/>
|
||||
<text x="20.54" y="321.07" class="st7" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>CCE-Cluster1</text> </g>
|
||||
<g id="shape44-62" v:mID="44" v:groupContext="shape" transform="translate(374.498,-253.265)">
|
||||
<title>工作表.44</title>
|
||||
<desc>POD</desc>
|
||||
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
|
||||
<v:textRect cx="28.3465" cy="316.272" width="56.7" height="34.0157"/>
|
||||
<rect x="0" y="299.264" width="56.6929" height="34.0157" class="st6"/>
|
||||
<text x="17.58" y="319.87" class="st8" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>POD</text> </g>
|
||||
<g id="shape45-65" v:mID="45" v:groupContext="shape" transform="translate(348.986,-236.257)">
|
||||
<title>工作表.45</title>
|
||||
<desc>nodeName: VK</desc>
|
||||
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
|
||||
<v:textRect cx="55.2756" cy="316.272" width="110.56" height="34.0157"/>
|
||||
<rect x="0" y="299.264" width="110.551" height="34.0157" class="st6"/>
|
||||
<text x="18.81" y="319.87" class="st8" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>nodeName: VK</text> </g>
|
||||
<g id="shape46-68" v:mID="46" v:groupContext="shape" transform="translate(432.608,-182.399)">
|
||||
<title>圆角的矩形.46</title>
|
||||
<v:userDefs>
|
||||
<v:ud v:nameU="CTypeTopLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeTopRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockHoriz" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockVert" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockDiag" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.13779527559055):1"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.13779527559055):1"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.13779527559055):1"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.13779527559055):1"/>
|
||||
</v:userDefs>
|
||||
<g id="shadow46-69" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
|
||||
transform="matrix(1,0,0,1,0.345598,1.97279)" class="st2">
|
||||
<path d="M9.92 333.28 L89.29 333.28 A9.9211 9.9211 -180 0 0 99.21 323.36 L99.21 303.52 A9.9211 9.9211 -180 0 0 89.29
|
||||
293.6 L9.92 293.6 A9.9211 9.9211 -180 0 0 0 303.52 L0 323.36 A9.9211 9.9211 -180 0 0 9.92 333.28 Z"
|
||||
class="st3"/>
|
||||
</g>
|
||||
<path d="M9.92 333.28 L89.29 333.28 A9.9211 9.9211 -180 0 0 99.21 323.36 L99.21 303.52 A9.9211 9.9211 -180 0 0 89.29
|
||||
293.6 L9.92 293.6 A9.9211 9.9211 -180 0 0 0 303.52 L0 323.36 A9.9211 9.9211 -180 0 0 9.92 333.28 Z"
|
||||
class="st9"/>
|
||||
</g>
|
||||
<g id="shape48-73" v:mID="48" v:groupContext="shape" transform="translate(706.728,-140.943) rotate(50.8696)">
|
||||
<title>工作表.48</title>
|
||||
<path d="M0 333.28 L53.9 333.28" class="st11"/>
|
||||
</g>
|
||||
<g id="shape50-78" v:mID="50" v:groupContext="shape" transform="translate(312.135,-155.693)">
|
||||
<title>圆角的矩形.50</title>
|
||||
<v:userDefs>
|
||||
<v:ud v:nameU="CTypeTopLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeTopRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockHoriz" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockVert" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockDiag" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.34251968503937):1"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.34251968503937):1"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.34251968503937):1"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.34251968503937):1"/>
|
||||
</v:userDefs>
|
||||
<path d="M24.66 333.28 L221.95 333.28 A24.661 24.661 -180 0 0 246.61 308.62 L246.61 206.51 A24.661 24.661 -180 0 0 221.95
|
||||
181.85 L24.66 181.85 A24.661 24.661 -180 0 0 -0 206.51 L0 308.62 A24.661 24.661 -180 0 0 24.66 333.28 Z"
|
||||
class="st1"/>
|
||||
</g>
|
||||
<g id="shape51-80" v:mID="51" v:groupContext="shape" transform="translate(400.009,-240.136)">
|
||||
<title>圆角的矩形.51</title>
|
||||
<v:userDefs>
|
||||
<v:ud v:nameU="CTypeTopLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeTopRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockHoriz" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockVert" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockDiag" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.11811023622047):1"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.11811023622047):1"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.11811023622047):1"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.11811023622047):1"/>
|
||||
</v:userDefs>
|
||||
<path d="M8.5 333.28 L76.54 333.28 A8.5038 8.5038 -180 0 0 85.04 324.78 L85.04 295.01 A8.5038 8.5038 -180 0 0 76.54 286.51
|
||||
L8.5 286.51 A8.5038 8.5038 -180 0 0 -0 295.01 L0 324.78 A8.5038 8.5038 -180 0 0 8.5 333.28 Z" class="st5"/>
|
||||
</g>
|
||||
<g id="shape52-82" v:mID="52" v:groupContext="shape" transform="translate(314.97,-278.404)">
|
||||
<title>工作表.52</title>
|
||||
<desc>CCE-ClusterN</desc>
|
||||
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
|
||||
<v:textRect cx="62.3622" cy="316.272" width="124.73" height="34.0157"/>
|
||||
<rect x="0" y="299.264" width="124.724" height="34.0157" class="st6"/>
|
||||
<text x="19.43" y="321.07" class="st7" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>CCE-ClusterN</text> </g>
|
||||
<g id="shape53-85" v:mID="53" v:groupContext="shape" transform="translate(411.348,-252.892)">
|
||||
<title>工作表.53</title>
|
||||
<desc>POD</desc>
|
||||
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
|
||||
<v:textRect cx="28.3465" cy="316.272" width="56.7" height="34.0157"/>
|
||||
<rect x="0" y="299.264" width="56.6929" height="34.0157" class="st6"/>
|
||||
<text x="17.58" y="319.87" class="st8" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>POD</text> </g>
|
||||
<g id="shape54-88" v:mID="54" v:groupContext="shape" transform="translate(385.836,-235.884)">
|
||||
<title>工作表.54</title>
|
||||
<desc>nodeName: VK</desc>
|
||||
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
|
||||
<v:textRect cx="55.2756" cy="316.272" width="110.56" height="34.0157"/>
|
||||
<rect x="0" y="299.264" width="110.551" height="34.0157" class="st6"/>
|
||||
<text x="18.81" y="319.87" class="st8" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>nodeName: VK</text> </g>
|
||||
<g id="shape55-91" v:mID="55" v:groupContext="shape" transform="translate(318.536,-181.69)">
|
||||
<title>圆角的矩形.55</title>
|
||||
<v:userDefs>
|
||||
<v:ud v:nameU="CTypeTopLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeTopRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockHoriz" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockVert" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockDiag" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.13779527559055):1"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.13779527559055):1"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.13779527559055):1"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.13779527559055):1"/>
|
||||
</v:userDefs>
|
||||
<g id="shadow55-92" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
|
||||
transform="matrix(1,0,0,1,0.345598,1.97279)" class="st2">
|
||||
<path d="M9.92 333.28 L89.29 333.28 A9.9211 9.9211 -180 0 0 99.21 323.36 L99.21 303.52 A9.9211 9.9211 -180 0 0 89.29
|
||||
293.6 L9.92 293.6 A9.9211 9.9211 -180 0 0 0 303.52 L0 323.36 A9.9211 9.9211 -180 0 0 9.92 333.28 Z"
|
||||
class="st3"/>
|
||||
</g>
|
||||
<path d="M9.92 333.28 L89.29 333.28 A9.9211 9.9211 -180 0 0 99.21 323.36 L99.21 303.52 A9.9211 9.9211 -180 0 0 89.29
|
||||
293.6 L9.92 293.6 A9.9211 9.9211 -180 0 0 0 303.52 L0 323.36 A9.9211 9.9211 -180 0 0 9.92 333.28 Z"
|
||||
class="st9"/>
|
||||
</g>
|
||||
<g id="shape56-96" v:mID="56" v:groupContext="shape" transform="translate(329.28,-185.233)">
|
||||
<title>工作表.56</title>
|
||||
<desc>Virtual-Kubelet</desc>
|
||||
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
|
||||
<v:textRect cx="42.5197" cy="316.272" width="85.04" height="34.0157"/>
|
||||
<rect x="0" y="299.264" width="85.0394" height="34.0157" class="st6"/>
|
||||
<text x="4.48" y="319.87" class="st10" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Virtual-Kubelet</text> </g>
|
||||
<g id="shape65-99" v:mID="65" v:groupContext="shape" transform="translate(164.734,-18.75)">
|
||||
<title>圆角的矩形.65</title>
|
||||
<v:userDefs>
|
||||
<v:ud v:nameU="CTypeTopLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeTopRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockHoriz" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockVert" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockDiag" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.34251968503937):1"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.34251968503937):1"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.34251968503937):1"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.34251968503937):1"/>
|
||||
</v:userDefs>
|
||||
<path d="M24.66 333.28 L221.95 333.28 A24.661 24.661 -180 0 0 246.61 308.62 L246.61 235.48 A24.661 24.661 -180 0 0 221.95
|
||||
210.82 L24.66 210.82 A24.661 24.661 -180 0 0 -0 235.48 L0 308.62 A24.661 24.661 -180 0 0 24.66 333.28 Z"
|
||||
class="st1"/>
|
||||
</g>
|
||||
<g id="shape66-101" v:mID="66" v:groupContext="shape" transform="translate(275.285,-220.17)">
|
||||
<title>工作表.66</title>
|
||||
<desc>. . .</desc>
|
||||
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
|
||||
<v:textRect cx="18.4252" cy="320.808" width="36.86" height="24.9449"/>
|
||||
<rect x="0" y="308.335" width="36.8504" height="24.9449" class="st6"/>
|
||||
<text x="9.65" y="325.01" class="st14" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>. . .</text> </g>
|
||||
<g id="shape67-104" v:mID="67" v:groupContext="shape" transform="translate(269.616,-113.386)">
|
||||
<title>工作表.67</title>
|
||||
<desc>CCI</desc>
|
||||
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
|
||||
<v:textRect cx="18.4252" cy="320.808" width="36.86" height="24.9449"/>
|
||||
<rect x="0" y="308.335" width="36.8504" height="24.9449" class="st6"/>
|
||||
<text x="7.82" y="325.61" class="st15" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>CCI</text> </g>
|
||||
<g id="shape68-107" v:mID="68" v:groupContext="shape" transform="translate(665.854,270.762) rotate(127.093)">
|
||||
<title>工作表.68</title>
|
||||
<path d="M0 333.28 L52.84 333.28" class="st11"/>
|
||||
</g>
|
||||
<g id="shape69-112" v:mID="69" v:groupContext="shape" transform="translate(181.417,-34.9075)">
|
||||
<title>圆角的矩形.69</title>
|
||||
<v:userDefs>
|
||||
<v:ud v:nameU="CTypeTopLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeTopRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockHoriz" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockVert" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockDiag" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.11417322834646):1"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.11417322834646):1"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.11417322834646):1"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.11417322834646):1"/>
|
||||
</v:userDefs>
|
||||
<path d="M8.22 333.28 L73.98 333.28 A8.22034 8.22034 -180 0 0 82.2 325.06 L82.2 263.55 A8.22034 8.22034 -180 0 0 73.98
|
||||
255.33 L8.22 255.33 A8.22034 8.22034 -180 0 0 0 263.55 L0 325.06 A8.22034 8.22034 -180 0 0 8.22 333.28 Z"
|
||||
class="st16"/>
|
||||
</g>
|
||||
<g id="shape70-114" v:mID="70" v:groupContext="shape" transform="translate(185.315,-34.9075)">
|
||||
<title>工作表.70</title>
|
||||
<desc>Project-VK1</desc>
|
||||
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
|
||||
<v:textRect cx="36.4961" cy="319.107" width="73" height="28.3465"/>
|
||||
<rect x="0" y="304.934" width="72.9921" height="28.3465" class="st6"/>
|
||||
<text x="7.77" y="322.71" class="st8" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Project-VK1</text> </g>
|
||||
<g id="shape71-117" v:mID="71" v:groupContext="shape" transform="translate(197.008,-60.4193)">
|
||||
<title>圆角的矩形.71</title>
|
||||
<v:userDefs>
|
||||
<v:ud v:nameU="CTypeTopLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeTopRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockHoriz" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockVert" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockDiag" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.07007239014478):1"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.07007239014478):1"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.07007239014478):1"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.07007239014478):1"/>
|
||||
</v:userDefs>
|
||||
<path d="M5.05 333.28 L45.41 333.28 A5.04513 5.04513 -180 0 0 50.45 328.23 L50.45 291.55 A5.04513 5.04513 -180 0 0 45.41
|
||||
286.51 L5.05 286.51 A5.04513 5.04513 -180 0 0 0 291.55 L0 328.23 A5.04513 5.04513 -180 0 0 5.05 333.28 Z"
|
||||
class="st17"/>
|
||||
</g>
|
||||
<g id="shape76-119" v:mID="76" v:groupContext="shape" transform="translate(202.814,-71.3327)">
|
||||
<title>工作表.76</title>
|
||||
<desc>POD</desc>
|
||||
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
|
||||
<v:textRect cx="18.4252" cy="320.808" width="36.86" height="24.9449"/>
|
||||
<rect x="0" y="308.335" width="36.8504" height="24.9449" class="st6"/>
|
||||
<text x="7.66" y="324.41" class="st8" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>POD</text> </g>
|
||||
<g id="shape77-122" v:mID="77" v:groupContext="shape" transform="translate(536.955,91.1513) rotate(80.0665)">
|
||||
<title>工作表.77</title>
|
||||
<path d="M0 333.28 L78.62 333.28" class="st11"/>
|
||||
</g>
|
||||
<g id="shape78-127" v:mID="78" v:groupContext="shape" transform="translate(311.528,-34.9075)">
|
||||
<title>圆角的矩形.78</title>
|
||||
<v:userDefs>
|
||||
<v:ud v:nameU="CTypeTopLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeTopRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockHoriz" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockVert" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockDiag" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.11417322834646):1"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.11417322834646):1"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.11417322834646):1"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.11417322834646):1"/>
|
||||
</v:userDefs>
|
||||
<path d="M8.22 333.28 L73.98 333.28 A8.22034 8.22034 -180 0 0 82.2 325.06 L82.2 263.55 A8.22034 8.22034 -180 0 0 73.98
|
||||
255.33 L8.22 255.33 A8.22034 8.22034 -180 0 0 0 263.55 L0 325.06 A8.22034 8.22034 -180 0 0 8.22 333.28 Z"
|
||||
class="st16"/>
|
||||
</g>
|
||||
<g id="shape79-129" v:mID="79" v:groupContext="shape" transform="translate(315.779,-34.9075)">
|
||||
<title>工作表.79</title>
|
||||
<desc>Project-VKN</desc>
|
||||
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
|
||||
<v:textRect cx="36.4961" cy="320.808" width="73" height="24.9449"/>
|
||||
<rect x="0" y="308.335" width="72.9921" height="24.9449" class="st6"/>
|
||||
<text x="6.94" y="324.41" class="st8" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>Project-VKN</text> </g>
|
||||
<g id="shape80-132" v:mID="80" v:groupContext="shape" transform="translate(328.535,-60.4193)">
|
||||
<title>圆角的矩形.80</title>
|
||||
<v:userDefs>
|
||||
<v:ud v:nameU="CTypeTopLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeTopRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockHoriz" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockVert" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockDiag" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.07007239014478):1"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.07007239014478):1"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.07007239014478):1"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.07007239014478):1"/>
|
||||
</v:userDefs>
|
||||
<path d="M5.05 333.28 L45.41 333.28 A5.04513 5.04513 -180 0 0 50.45 328.23 L50.45 291.55 A5.04513 5.04513 -180 0 0 45.41
|
||||
286.51 L5.05 286.51 A5.04513 5.04513 -180 0 0 0 291.55 L0 328.23 A5.04513 5.04513 -180 0 0 5.05 333.28 Z"
|
||||
class="st17"/>
|
||||
</g>
|
||||
<g id="shape81-134" v:mID="81" v:groupContext="shape" transform="translate(334.342,-71.3327)">
|
||||
<title>工作表.81</title>
|
||||
<desc>POD</desc>
|
||||
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
|
||||
<v:textRect cx="18.4252" cy="320.808" width="36.86" height="24.9449"/>
|
||||
<rect x="0" y="308.335" width="36.8504" height="24.9449" class="st6"/>
|
||||
<text x="7.66" y="324.41" class="st8" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>POD</text> </g>
|
||||
<g id="shape82-137" v:mID="82" v:groupContext="shape" transform="translate(695.381,214.76) rotate(100.926)">
|
||||
<title>工作表.82</title>
|
||||
<path d="M0 333.28 L75.87 333.28" class="st11"/>
|
||||
</g>
|
||||
<g id="shape83-142" v:mID="83" v:groupContext="shape" transform="translate(269.616,-68.5984)">
|
||||
<title>工作表.83</title>
|
||||
<desc>. . .</desc>
|
||||
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
|
||||
<v:textRect cx="18.4252" cy="320.808" width="36.86" height="24.9449"/>
|
||||
<rect x="0" y="308.335" width="36.8504" height="24.9449" class="st6"/>
|
||||
<text x="9.65" y="325.01" class="st14" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>. . .</text> </g>
|
||||
<g id="shape86-145" v:mID="86" v:groupContext="shape" transform="translate(64.6135,-169.493)">
|
||||
<title>工作表.86</title>
|
||||
<desc>node</desc>
|
||||
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
|
||||
<v:textRect cx="25.2261" cy="320.808" width="50.46" height="24.9449"/>
|
||||
<rect x="0" y="308.335" width="50.4521" height="24.9449" class="st6"/>
|
||||
<text x="10.7" y="325.01" class="st18" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>node</text> </g>
|
||||
<g id="shape87-148" v:mID="87" v:groupContext="shape" transform="translate(431.191,-170.09)">
|
||||
<title>圆角的矩形.87</title>
|
||||
<v:userDefs>
|
||||
<v:ud v:nameU="CTypeTopLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeTopRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockHoriz" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockVert" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockDiag" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
|
||||
</v:userDefs>
|
||||
<g id="shadow87-149" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
|
||||
transform="matrix(1,0,0,1,0.345598,1.97279)" class="st2">
|
||||
<path d="M11.34 333.28 L102.05 333.28 A11.3384 11.3384 -180 0 0 113.39 321.94 L113.39 287.93 A11.3384 11.3384 -180
|
||||
0 0 102.05 276.59 L11.34 276.59 A11.3384 11.3384 -180 0 0 0 287.93 L0 321.94 A11.3384 11.3384 -180 0
|
||||
0 11.34 333.28 Z" class="st3"/>
|
||||
</g>
|
||||
<path d="M11.34 333.28 L102.05 333.28 A11.3384 11.3384 -180 0 0 113.39 321.94 L113.39 287.93 A11.3384 11.3384 -180 0
|
||||
0 102.05 276.59 L11.34 276.59 A11.3384 11.3384 -180 0 0 0 287.93 L0 321.94 A11.3384 11.3384 -180 0 0 11.34
|
||||
333.28 Z" class="st4"/>
|
||||
</g>
|
||||
<g id="shape88-153" v:mID="88" v:groupContext="shape" transform="translate(491.958,-188.739)">
|
||||
<title>圆角的矩形.88</title>
|
||||
<v:userDefs>
|
||||
<v:ud v:nameU="CTypeTopLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeTopRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockHoriz" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockVert" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockDiag" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.061023622047244):1"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.061023622047244):1"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.061023622047244):1"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.061023622047244):1"/>
|
||||
</v:userDefs>
|
||||
<g id="shadow88-154" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
|
||||
transform="matrix(1,0,0,1,0.345598,1.97279)" class="st2">
|
||||
<path d="M4.39 333.28 L39.54 333.28 A4.39363 4.39363 -180 0 0 43.94 328.89 L43.94 309.33 A4.39363 4.39363 -180 0
|
||||
0 39.54 304.93 L4.39 304.93 A4.39363 4.39363 -180 0 0 0 309.33 L0 328.89 A4.39363 4.39363 -180 0 0 4.39
|
||||
333.28 Z" class="st3"/>
|
||||
</g>
|
||||
<path d="M4.39 333.28 L39.54 333.28 A4.39363 4.39363 -180 0 0 43.94 328.89 L43.94 309.33 A4.39363 4.39363 -180 0 0 39.54
|
||||
304.93 L4.39 304.93 A4.39363 4.39363 -180 0 0 0 309.33 L0 328.89 A4.39363 4.39363 -180 0 0 4.39 333.28 Z"
|
||||
class="st13"/>
|
||||
</g>
|
||||
<g id="shape89-158" v:mID="89" v:groupContext="shape" transform="translate(496.21,-190.723)">
|
||||
<title>工作表.89</title>
|
||||
<desc>POD</desc>
|
||||
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
|
||||
<v:textRect cx="18.4252" cy="320.808" width="36.86" height="24.9449"/>
|
||||
<rect x="0" y="308.335" width="36.8504" height="24.9449" class="st6"/>
|
||||
<text x="7.66" y="324.41" class="st8" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>POD</text> </g>
|
||||
<g id="shape90-161" v:mID="90" v:groupContext="shape" transform="translate(440.58,-188.739)">
|
||||
<title>圆角的矩形.90</title>
|
||||
<v:userDefs>
|
||||
<v:ud v:nameU="CTypeTopLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeTopRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotLeftSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CTypeBotRightSnip" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockHoriz" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockVert" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="CornerLockDiag" v:prompt="" v:val="VT0(0):5"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.15748031496063):24"/>
|
||||
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
|
||||
<v:ud v:nameU="TopLeftOffset" v:prompt="" v:val="VT0(0.061023622047244):1"/>
|
||||
<v:ud v:nameU="TopRightOffset" v:prompt="" v:val="VT0(0.061023622047244):1"/>
|
||||
<v:ud v:nameU="BotLeftOffset" v:prompt="" v:val="VT0(0.061023622047244):1"/>
|
||||
<v:ud v:nameU="BotRightOffset" v:prompt="" v:val="VT0(0.061023622047244):1"/>
|
||||
</v:userDefs>
|
||||
<g id="shadow90-162" v:groupContext="shadow" v:shadowOffsetX="0.345598" v:shadowOffsetY="-1.97279" v:shadowType="1"
|
||||
transform="matrix(1,0,0,1,0.345598,1.97279)" class="st2">
|
||||
<path d="M4.39 333.28 L39.54 333.28 A4.39363 4.39363 -180 0 0 43.94 328.89 L43.94 309.33 A4.39363 4.39363 -180 0
|
||||
0 39.54 304.93 L4.39 304.93 A4.39363 4.39363 -180 0 0 0 309.33 L0 328.89 A4.39363 4.39363 -180 0 0 4.39
|
||||
333.28 Z" class="st3"/>
|
||||
</g>
|
||||
<path d="M4.39 333.28 L39.54 333.28 A4.39363 4.39363 -180 0 0 43.94 328.89 L43.94 309.33 A4.39363 4.39363 -180 0 0 39.54
|
||||
304.93 L4.39 304.93 A4.39363 4.39363 -180 0 0 0 309.33 L0 328.89 A4.39363 4.39363 -180 0 0 4.39 333.28 Z"
|
||||
class="st13"/>
|
||||
</g>
|
||||
<g id="shape91-166" v:mID="91" v:groupContext="shape" transform="translate(442.706,-189.306)">
|
||||
<title>工作表.91</title>
|
||||
<desc>POD</desc>
|
||||
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
|
||||
<v:textRect cx="18.4252" cy="320.808" width="36.86" height="24.9449"/>
|
||||
<rect x="0" y="308.335" width="36.8504" height="24.9449" class="st6"/>
|
||||
<text x="7.66" y="324.41" class="st8" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>POD</text> </g>
|
||||
<g id="shape92-169" v:mID="92" v:groupContext="shape" transform="translate(462.657,-165.614)">
|
||||
<title>工作表.92</title>
|
||||
<desc>node</desc>
|
||||
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
|
||||
<v:textRect cx="25.2261" cy="320.808" width="50.46" height="24.9449"/>
|
||||
<rect x="0" y="308.335" width="50.4521" height="24.9449" class="st6"/>
|
||||
<text x="10.7" y="325.01" class="st18" v:langID="1033"><v:paragraph v:horizAlign="1"/><v:tabList/>node</text> </g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 51 KiB |
412
providers/huawei/cci.go
Normal file
412
providers/huawei/cci.go
Normal file
@@ -0,0 +1,412 @@
|
||||
package huawei
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/virtual-kubelet/virtual-kubelet/manager"
|
||||
"github.com/virtual-kubelet/virtual-kubelet/providers/huawei/auth"
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
const (
|
||||
podAnnotationNamespaceKey = "virtual-kubelet-namespace"
|
||||
podAnnotationPodNameKey = "virtual-kubelet-podname"
|
||||
podAnnotationClusterNameKey = "virtual-kubelet-clustername"
|
||||
podAnnotationUIDkey = "virtual-kubelet-uid"
|
||||
podAnnotationNodeName = "virtual-kubelet-nodename"
|
||||
podAnnotationCreationTimestamp = "virtual-kubelet-creationtimestamp"
|
||||
)
|
||||
|
||||
var defaultApiEndpoint string = "https://cciback.cn-north-1.huaweicloud.com"
|
||||
|
||||
// CCIProvider implements the virtual-kubelet provider interface and communicates with Huawei's CCI APIs.
|
||||
type CCIProvider struct {
|
||||
appKey string
|
||||
appSecret string
|
||||
apiEndpoint string
|
||||
region string
|
||||
service string
|
||||
project string
|
||||
internalIP string
|
||||
daemonEndpointPort int32
|
||||
nodeName string
|
||||
operatingSystem string
|
||||
client *Client
|
||||
resourceManager *manager.ResourceManager
|
||||
cpu string
|
||||
memory string
|
||||
pods string
|
||||
}
|
||||
|
||||
// Client represents the client config for Huawei.
|
||||
type Client struct {
|
||||
Signer auth.Signer
|
||||
HTTPClient http.Client
|
||||
}
|
||||
|
||||
// NewCCIProvider creates a new CCI provider.
|
||||
func NewCCIProvider(config string, rm *manager.ResourceManager, nodeName, operatingSystem string, internalIP string, daemonEndpointPort int32) (*CCIProvider, error) {
|
||||
p := CCIProvider{}
|
||||
|
||||
if config != "" {
|
||||
f, err := os.Open(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if err := p.loadConfig(f); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if appKey := os.Getenv("CCI_APP_KEP"); appKey != "" {
|
||||
p.appKey = appKey
|
||||
}
|
||||
if p.appKey == "" {
|
||||
return nil, errors.New("AppKey can not be empty please set CCI_APP_KEP")
|
||||
}
|
||||
if appSecret := os.Getenv("CCI_APP_SECRET"); appSecret != "" {
|
||||
p.appSecret = appSecret
|
||||
}
|
||||
if p.appSecret == "" {
|
||||
return nil, errors.New("AppSecret can not be empty please set CCI_APP_SECRET")
|
||||
}
|
||||
p.client = new(Client)
|
||||
p.client.Signer = &auth.SignerHws{
|
||||
AppKey: p.appKey,
|
||||
AppSecret: p.appSecret,
|
||||
Region: p.region,
|
||||
Service: p.service,
|
||||
}
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
p.client.HTTPClient = http.Client{
|
||||
Transport: tr,
|
||||
}
|
||||
p.resourceManager = rm
|
||||
p.apiEndpoint = defaultApiEndpoint
|
||||
p.nodeName = nodeName
|
||||
p.operatingSystem = operatingSystem
|
||||
p.internalIP = internalIP
|
||||
p.daemonEndpointPort = daemonEndpointPort
|
||||
|
||||
if err := p.createProject(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &p, nil
|
||||
}
|
||||
|
||||
func (p *CCIProvider) createProject() error {
|
||||
// Create the createProject request url
|
||||
uri := p.apiEndpoint + "/api/v1/namespaces"
|
||||
// build the request
|
||||
project := &v1.Namespace{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Namespace",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: p.project,
|
||||
},
|
||||
}
|
||||
var bodyReader io.Reader
|
||||
body, err := json.Marshal(project)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if body != nil {
|
||||
bodyReader = bytes.NewReader(body)
|
||||
}
|
||||
|
||||
r, err := http.NewRequest("POST", uri, bodyReader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = p.signRequest(r); err != nil {
|
||||
return fmt.Errorf("Sign the request failed: %v", err)
|
||||
}
|
||||
_, err = p.client.HTTPClient.Do(r)
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *CCIProvider) signRequest(r *http.Request) error {
|
||||
r.Header.Add("content-type", "application/json; charset=utf-8")
|
||||
if err := p.client.Signer.Sign(r); err != nil {
|
||||
return fmt.Errorf("Sign the request failed: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *CCIProvider) setPodAnnotations(pod *v1.Pod) {
|
||||
metav1.SetMetaDataAnnotation(&pod.ObjectMeta, podAnnotationNamespaceKey, pod.Namespace)
|
||||
metav1.SetMetaDataAnnotation(&pod.ObjectMeta, podAnnotationClusterNameKey, pod.ClusterName)
|
||||
metav1.SetMetaDataAnnotation(&pod.ObjectMeta, podAnnotationPodNameKey, pod.Name)
|
||||
metav1.SetMetaDataAnnotation(&pod.ObjectMeta, podAnnotationUIDkey, string(pod.UID))
|
||||
metav1.SetMetaDataAnnotation(&pod.ObjectMeta, podAnnotationNodeName, pod.Spec.NodeName)
|
||||
metav1.SetMetaDataAnnotation(&pod.ObjectMeta, podAnnotationCreationTimestamp, pod.CreationTimestamp.String())
|
||||
pod.Namespace = p.project
|
||||
pod.Name = pod.Namespace + "-" + pod.Name
|
||||
pod.UID = ""
|
||||
pod.Spec.NodeName = ""
|
||||
pod.CreationTimestamp = metav1.Time{}
|
||||
}
|
||||
|
||||
func (p *CCIProvider) deletePodAnnotations(pod *v1.Pod) error {
|
||||
pod.Name = pod.Annotations[podAnnotationPodNameKey]
|
||||
pod.Namespace = pod.Annotations[podAnnotationNamespaceKey]
|
||||
pod.UID = types.UID(pod.Annotations[podAnnotationUIDkey])
|
||||
pod.ClusterName = pod.Annotations[podAnnotationClusterNameKey]
|
||||
pod.Spec.NodeName = pod.Annotations[podAnnotationNodeName]
|
||||
if pod.Annotations[podAnnotationCreationTimestamp] != "" {
|
||||
t, err := time.Parse("2006-01-02 15:04:05.999999999 -0700 MST", pod.Annotations[podAnnotationCreationTimestamp])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
podCreationTimestamp := metav1.NewTime(t)
|
||||
pod.CreationTimestamp = podCreationTimestamp
|
||||
}
|
||||
delete(pod.Annotations, podAnnotationPodNameKey)
|
||||
delete(pod.Annotations, podAnnotationNamespaceKey)
|
||||
delete(pod.Annotations, podAnnotationUIDkey)
|
||||
delete(pod.Annotations, podAnnotationClusterNameKey)
|
||||
delete(pod.Annotations, podAnnotationNodeName)
|
||||
delete(pod.Annotations, podAnnotationCreationTimestamp)
|
||||
pod.Annotations = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreatePod takes a Kubernetes Pod and deploys it within the huawei CCI provider.
|
||||
func (p *CCIProvider) CreatePod(pod *v1.Pod) error {
|
||||
// Create the createPod request url
|
||||
p.setPodAnnotations(pod)
|
||||
uri := p.apiEndpoint + "/api/v1/namespaces/" + p.project + "/pods"
|
||||
// build the request
|
||||
var bodyReader io.Reader
|
||||
body, err := json.Marshal(pod)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if body != nil {
|
||||
bodyReader = bytes.NewReader(body)
|
||||
}
|
||||
|
||||
r, err := http.NewRequest("POST", uri, bodyReader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = p.signRequest(r); err != nil {
|
||||
return fmt.Errorf("Sign the request failed: %v", err)
|
||||
}
|
||||
_, err = p.client.HTTPClient.Do(r)
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdatePod takes a Kubernetes Pod and updates it within the huawei CCI provider.
|
||||
func (p *CCIProvider) UpdatePod(pod *v1.Pod) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeletePod takes a Kubernetes Pod and deletes it from the huawei CCI provider.
|
||||
func (p *CCIProvider) DeletePod(pod *v1.Pod) error {
|
||||
// Create the deletePod request url
|
||||
podName := pod.Namespace + "-" + pod.Name
|
||||
uri := p.apiEndpoint + "/api/v1/namespaces/" + p.project + "/pods/" + podName
|
||||
// build the request
|
||||
r, err := http.NewRequest("DELETE", uri, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = p.signRequest(r); err != nil {
|
||||
return fmt.Errorf("Sign the request failed: %v", err)
|
||||
}
|
||||
_, err = p.client.HTTPClient.Do(r)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetPod retrieves a pod by name from the huawei CCI provider.
|
||||
func (p *CCIProvider) GetPod(namespace, name string) (*v1.Pod, error) {
|
||||
// Create the getPod request url
|
||||
podName := namespace + "-" + name
|
||||
uri := p.apiEndpoint + "/api/v1/namespaces/" + p.project + "/pods/" + podName
|
||||
r, err := http.NewRequest("GET", uri, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Create get POD request failed: %v", err)
|
||||
}
|
||||
|
||||
if err = p.signRequest(r); err != nil {
|
||||
return nil, fmt.Errorf("Sign the request failed: %v", err)
|
||||
}
|
||||
|
||||
resp, err := p.client.HTTPClient.Do(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var pod v1.Pod
|
||||
if err = json.Unmarshal(body, &pod); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := p.deletePodAnnotations(&pod); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pod, nil
|
||||
}
|
||||
|
||||
// GetContainerLogs retrieves the logs of a container by name from the huawei CCI provider.
|
||||
func (p *CCIProvider) GetContainerLogs(namespace, podName, containerName string, tail int) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// GetPodStatus retrieves the status of a pod by name from the huawei CCI provider.
|
||||
func (p *CCIProvider) GetPodStatus(namespace, name string) (*v1.PodStatus, error) {
|
||||
pod, err := p.GetPod(namespace, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pod == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &pod.Status, nil
|
||||
}
|
||||
|
||||
// GetPods retrieves a list of all pods running on the huawei CCI provider.
|
||||
func (p *CCIProvider) GetPods() ([]*v1.Pod, error) {
|
||||
// Create the getPod request url
|
||||
uri := p.apiEndpoint + "/api/v1/namespaces/" + p.project + "/pods"
|
||||
r, err := http.NewRequest("GET", uri, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Create get POD request failed: %v", err)
|
||||
}
|
||||
|
||||
if err = p.signRequest(r); err != nil {
|
||||
return nil, fmt.Errorf("Sign the request failed: %v", err)
|
||||
}
|
||||
resp, err := p.client.HTTPClient.Do(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var pods []*v1.Pod
|
||||
if err = json.Unmarshal(body, &pods); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, pod := range pods {
|
||||
if err := p.deletePodAnnotations(pod); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return pods, nil
|
||||
}
|
||||
|
||||
// Capacity returns a resource list with the capacity constraints of the huawei CCI provider.
|
||||
func (p *CCIProvider) Capacity() v1.ResourceList {
|
||||
return v1.ResourceList{
|
||||
"cpu": resource.MustParse(p.cpu),
|
||||
"memory": resource.MustParse(p.memory),
|
||||
"pods": resource.MustParse(p.pods),
|
||||
}
|
||||
}
|
||||
|
||||
// NodeConditions returns a list of conditions (Ready, OutOfDisk, etc), which is
|
||||
// polled periodically to update the node status within Kubernetes.
|
||||
func (p *CCIProvider) NodeConditions() []v1.NodeCondition {
|
||||
// TODO: Make these dynamic and augment with custom CCI specific conditions of interest
|
||||
return []v1.NodeCondition{
|
||||
{
|
||||
Type: "Ready",
|
||||
Status: v1.ConditionTrue,
|
||||
LastHeartbeatTime: metav1.Now(),
|
||||
LastTransitionTime: metav1.Now(),
|
||||
Reason: "KubeletReady",
|
||||
Message: "kubelet is ready.",
|
||||
},
|
||||
{
|
||||
Type: "OutOfDisk",
|
||||
Status: v1.ConditionFalse,
|
||||
LastHeartbeatTime: metav1.Now(),
|
||||
LastTransitionTime: metav1.Now(),
|
||||
Reason: "KubeletHasSufficientDisk",
|
||||
Message: "kubelet has sufficient disk space available",
|
||||
},
|
||||
{
|
||||
Type: "MemoryPressure",
|
||||
Status: v1.ConditionFalse,
|
||||
LastHeartbeatTime: metav1.Now(),
|
||||
LastTransitionTime: metav1.Now(),
|
||||
Reason: "KubeletHasSufficientMemory",
|
||||
Message: "kubelet has sufficient memory available",
|
||||
},
|
||||
{
|
||||
Type: "DiskPressure",
|
||||
Status: v1.ConditionFalse,
|
||||
LastHeartbeatTime: metav1.Now(),
|
||||
LastTransitionTime: metav1.Now(),
|
||||
Reason: "KubeletHasNoDiskPressure",
|
||||
Message: "kubelet has no disk pressure",
|
||||
},
|
||||
{
|
||||
Type: "NetworkUnavailable",
|
||||
Status: v1.ConditionFalse,
|
||||
LastHeartbeatTime: metav1.Now(),
|
||||
LastTransitionTime: metav1.Now(),
|
||||
Reason: "RouteCreated",
|
||||
Message: "RouteController created a route",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// NodeAddresses returns a list of addresses for the node status
|
||||
// within Kubernetes.
|
||||
func (p *CCIProvider) NodeAddresses() []v1.NodeAddress {
|
||||
// TODO: Make these dynamic and augment with custom CCI specific conditions of interest
|
||||
return []v1.NodeAddress{
|
||||
{
|
||||
Type: "InternalIP",
|
||||
Address: p.internalIP,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// NodeDaemonEndpoints returns NodeDaemonEndpoints for the node status
|
||||
// within Kubernetes.
|
||||
func (p *CCIProvider) NodeDaemonEndpoints() *v1.NodeDaemonEndpoints {
|
||||
return &v1.NodeDaemonEndpoints{
|
||||
KubeletEndpoint: v1.DaemonEndpoint{
|
||||
Port: p.daemonEndpointPort,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// OperatingSystem returns the operating system the huawei CCI provider is for.
|
||||
func (p *CCIProvider) OperatingSystem() string {
|
||||
return p.operatingSystem
|
||||
}
|
||||
9
providers/huawei/cci.toml
Normal file
9
providers/huawei/cci.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
# example configuration file for Huawei CCI virtual-kubelet provider.
|
||||
|
||||
Project = "default"
|
||||
Region = "southchina"
|
||||
Service = "CCI"
|
||||
OperatingSystem = "Linux"
|
||||
CPU = "20"
|
||||
Memory = "100Gi"
|
||||
Pods = "20"
|
||||
151
providers/huawei/cciMock.go
Normal file
151
providers/huawei/cciMock.go
Normal file
@@ -0,0 +1,151 @@
|
||||
package huawei
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
// CCIMock implements a CCI service mock server.
|
||||
type CCIMock struct {
|
||||
server *httptest.Server
|
||||
OnCreateProject func(*v1.Namespace) (int, interface{})
|
||||
OnCreatePod func(*v1.Pod) (int, interface{})
|
||||
OnGetPods func() (int, interface{})
|
||||
OnGetPod func(string, string) (int, interface{})
|
||||
}
|
||||
|
||||
// fakeSigner signature HWS meta
|
||||
type fakeSigner struct {
|
||||
AppKey string
|
||||
AppSecret string
|
||||
Region string
|
||||
Service string
|
||||
}
|
||||
|
||||
// Sign set Authorization header
|
||||
func (s *fakeSigner) Sign(r *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
cciProjectRoute = "/api/v1/namespaces"
|
||||
cciPodsRoute = cciProjectRoute + "/{namespaceID}/pods"
|
||||
cciPodRoute = cciPodsRoute + "/{podID}"
|
||||
)
|
||||
|
||||
// NewCCIMock creates a CCI service mock server.
|
||||
func NewCCIMock() *CCIMock {
|
||||
mock := new(CCIMock)
|
||||
mock.start()
|
||||
|
||||
return mock
|
||||
}
|
||||
|
||||
// Start the CCI service mock service.
|
||||
func (mock *CCIMock) start() {
|
||||
if mock.server != nil {
|
||||
return
|
||||
}
|
||||
|
||||
router := mux.NewRouter()
|
||||
router.HandleFunc(
|
||||
cciProjectRoute,
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
var ns v1.Namespace
|
||||
if err := json.NewDecoder(r.Body).Decode(&ns); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if mock.OnCreatePod != nil {
|
||||
statusCode, response := mock.OnCreateProject(&ns)
|
||||
w.WriteHeader(statusCode)
|
||||
b := new(bytes.Buffer)
|
||||
json.NewEncoder(b).Encode(response)
|
||||
w.Write(b.Bytes())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNotImplemented)
|
||||
}).Methods("PUT")
|
||||
|
||||
router.HandleFunc(
|
||||
cciPodsRoute,
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
var pod v1.Pod
|
||||
if err := json.NewDecoder(r.Body).Decode(&pod); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if mock.OnCreatePod != nil {
|
||||
statusCode, response := mock.OnCreatePod(&pod)
|
||||
w.WriteHeader(statusCode)
|
||||
b := new(bytes.Buffer)
|
||||
json.NewEncoder(b).Encode(response)
|
||||
w.Write(b.Bytes())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNotImplemented)
|
||||
}).Methods("PUT")
|
||||
|
||||
router.HandleFunc(
|
||||
cciPodRoute,
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
namespace, _ := mux.Vars(r)["namespaceID"]
|
||||
podname, _ := mux.Vars(r)["podID"]
|
||||
|
||||
if mock.OnGetPod != nil {
|
||||
statusCode, response := mock.OnGetPod(namespace, podname)
|
||||
w.WriteHeader(statusCode)
|
||||
b := new(bytes.Buffer)
|
||||
json.NewEncoder(b).Encode(response)
|
||||
w.Write(b.Bytes())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNotImplemented)
|
||||
}).Methods("GET")
|
||||
|
||||
router.HandleFunc(
|
||||
cciPodsRoute,
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
if mock.OnGetPods != nil {
|
||||
statusCode, response := mock.OnGetPods()
|
||||
w.WriteHeader(statusCode)
|
||||
b := new(bytes.Buffer)
|
||||
json.NewEncoder(b).Encode(response)
|
||||
w.Write(b.Bytes())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNotImplemented)
|
||||
}).Methods("GET")
|
||||
|
||||
mock.server = httptest.NewServer(router)
|
||||
}
|
||||
|
||||
// GetServerURL returns the mock server URL.
|
||||
func (mock *CCIMock) GetServerURL() string {
|
||||
if mock.server != nil {
|
||||
return mock.server.URL
|
||||
}
|
||||
|
||||
panic("Mock server is not initialized.")
|
||||
}
|
||||
|
||||
// Close terminates the CCI mock server.
|
||||
func (mock *CCIMock) Close() {
|
||||
if mock.server != nil {
|
||||
mock.server.Close()
|
||||
mock.server = nil
|
||||
}
|
||||
}
|
||||
212
providers/huawei/cci_test.go
Normal file
212
providers/huawei/cci_test.go
Normal file
@@ -0,0 +1,212 @@
|
||||
package huawei
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/uuid"
|
||||
)
|
||||
|
||||
const (
|
||||
fakeAppKey = "Whj8f5RAHsvQahveqCdo"
|
||||
fakeAppSecret = "ymW5JgrdwrIvRS76YxyIqHNXe9s5ocIhaWWvPUhx"
|
||||
fakeRegion = "southchina"
|
||||
fakeService = "default"
|
||||
fakeProject = "vk-project"
|
||||
fakeNodeName = "vk"
|
||||
)
|
||||
|
||||
// TestCreateProject test create project.
|
||||
func TestCreateProject(t *testing.T) {
|
||||
cciServerMocker, provider, err := prepareMocks()
|
||||
|
||||
if err != nil {
|
||||
t.Fatal("Unable to prepare the mocks", err)
|
||||
}
|
||||
|
||||
cciServerMocker.OnCreateProject = func(ns *v1.Namespace) (int, interface{}) {
|
||||
assert.NotNil(t, ns, "Project is nil")
|
||||
assert.Equal(t, fakeProject, ns.Name, "pod.Annotations[\"virtual-kubelet-podname\"] is not expected")
|
||||
return http.StatusOK, &v1.Namespace{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Namespace",
|
||||
APIVersion: "v1",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fakeProject,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if err := provider.createProject(); err != nil {
|
||||
t.Fatal("Failed to create project", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCreatePod test create pod.
|
||||
func TestCreatePod(t *testing.T) {
|
||||
cciServerMocker, provider, err := prepareMocks()
|
||||
|
||||
if err != nil {
|
||||
t.Fatal("Unable to prepare the mocks", err)
|
||||
}
|
||||
podName := "pod-" + string(uuid.NewUUID())
|
||||
podNamespace := "ns-" + string(uuid.NewUUID())
|
||||
|
||||
cciServerMocker.OnCreatePod = func(pod *v1.Pod) (int, interface{}) {
|
||||
assert.NotNil(t, pod, "Pod is nil")
|
||||
assert.NotNil(t, pod.Annotations, "pod.Annotations is expected")
|
||||
assert.Equal(t, podName, pod.Annotations[podAnnotationPodNameKey], "pod.Annotations[\"virtual-kubelet-podname\"] is not expected")
|
||||
assert.Equal(t, podNamespace, pod.Annotations[podAnnotationNamespaceKey], "pod.Annotations[\"virtual-kubelet-namespace\"] is not expected")
|
||||
assert.Equal(t, 1, len(pod.Spec.Containers), "1 Container is expected")
|
||||
assert.Equal(t, "nginx", pod.Spec.Containers[0].Name, "Container nginx is expected")
|
||||
return http.StatusOK, pod
|
||||
}
|
||||
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: podName,
|
||||
Namespace: podNamespace,
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
v1.Container{
|
||||
Name: "nginx",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := provider.CreatePod(pod); err != nil {
|
||||
t.Fatal("Failed to create pod", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests get pod.
|
||||
func TestGetPod(t *testing.T) {
|
||||
cciServerMocker, provider, err := prepareMocks()
|
||||
|
||||
if err != nil {
|
||||
t.Fatal("Unable to prepare the mocks", err)
|
||||
}
|
||||
|
||||
podName := "pod-" + string(uuid.NewUUID())
|
||||
podNamespace := "ns-" + string(uuid.NewUUID())
|
||||
|
||||
cciServerMocker.OnGetPod = func(namespace, name string) (int, interface{}) {
|
||||
annotations := map[string]string{
|
||||
podAnnotationPodNameKey: "podname",
|
||||
podAnnotationNamespaceKey: "podnamespaces",
|
||||
podAnnotationUIDkey: "poduid",
|
||||
podAnnotationClusterNameKey: "podclustername",
|
||||
podAnnotationNodeName: "podnodename",
|
||||
}
|
||||
|
||||
return http.StatusOK, &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: podName,
|
||||
Namespace: podNamespace,
|
||||
Annotations: annotations,
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
v1.Container{
|
||||
Name: "nginx",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
pod, err := provider.GetPod(podNamespace, podName)
|
||||
if err != nil {
|
||||
t.Fatal("Failed to get pod", err)
|
||||
}
|
||||
|
||||
assert.NotNil(t, pod, "Response pod should not be nil")
|
||||
assert.NotNil(t, pod.Spec.Containers, "Containers should not be nil")
|
||||
assert.Equal(t, pod.Name, "podname", "Pod name is not expected")
|
||||
assert.Equal(t, pod.Namespace, "podnamespaces", "Pod namespace is not expected")
|
||||
assert.Nil(t, pod.Annotations, "Pod Annotations should be nil")
|
||||
assert.Equal(t, string(pod.UID), "poduid", "Pod UID is not expected")
|
||||
assert.Equal(t, pod.ClusterName, "podclustername", "Pod clustername is not expected")
|
||||
assert.Equal(t, pod.Spec.NodeName, "podnodename", "Pod node name is not expected")
|
||||
}
|
||||
|
||||
// Tests get pod.
|
||||
func TestGetPods(t *testing.T) {
|
||||
cciServerMocker, provider, err := prepareMocks()
|
||||
|
||||
if err != nil {
|
||||
t.Fatal("Unable to prepare the mocks", err)
|
||||
}
|
||||
|
||||
podName := "pod-" + string(uuid.NewUUID())
|
||||
podNamespace := "ns-" + string(uuid.NewUUID())
|
||||
|
||||
cciServerMocker.OnGetPods = func() (int, interface{}) {
|
||||
annotations := map[string]string{
|
||||
podAnnotationPodNameKey: "podname",
|
||||
podAnnotationNamespaceKey: "podnamespaces",
|
||||
podAnnotationUIDkey: "poduid",
|
||||
podAnnotationClusterNameKey: "podclustername",
|
||||
podAnnotationNodeName: "podnodename",
|
||||
}
|
||||
|
||||
pod := v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: podName,
|
||||
Namespace: podNamespace,
|
||||
Annotations: annotations,
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{
|
||||
v1.Container{
|
||||
Name: "nginx",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return http.StatusOK, []v1.Pod{pod}
|
||||
}
|
||||
pods, err := provider.GetPods()
|
||||
if err != nil {
|
||||
t.Fatal("Failed to get pods", err)
|
||||
}
|
||||
|
||||
pod := pods[0]
|
||||
assert.NotNil(t, pod, "Response pod should not be nil")
|
||||
assert.NotNil(t, pod.Spec.Containers, "Containers should not be nil")
|
||||
assert.Equal(t, pod.Name, "podname", "Pod name is not expected")
|
||||
assert.Equal(t, pod.Namespace, "podnamespaces", "Pod namespace is not expected")
|
||||
assert.Nil(t, pod.Annotations, "Pod Annotations should be nil")
|
||||
assert.Equal(t, string(pod.UID), "poduid", "Pod UID is not expected")
|
||||
assert.Equal(t, pod.ClusterName, "podclustername", "Pod clustername is not expected")
|
||||
assert.Equal(t, pod.Spec.NodeName, "podnodename", "Pod node name is not expected")
|
||||
}
|
||||
|
||||
func prepareMocks() (*CCIMock, *CCIProvider, error) {
|
||||
cciServerMocker := NewCCIMock()
|
||||
|
||||
os.Setenv("CCI_APP_KEP", fakeAppKey)
|
||||
os.Setenv("CCI_APP_SECRET", fakeAppSecret)
|
||||
|
||||
defaultApiEndpoint = cciServerMocker.GetServerURL()
|
||||
provider, err := NewCCIProvider("cci.toml", nil, fakeNodeName, "Linux", "0.0.0.0", 10250)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
provider.project = fakeProject
|
||||
provider.client.Signer = &fakeSigner{
|
||||
AppKey: fakeAppKey,
|
||||
AppSecret: fakeAppSecret,
|
||||
Region: fakeRegion,
|
||||
Service: fakeService,
|
||||
}
|
||||
|
||||
return cciServerMocker, provider, nil
|
||||
}
|
||||
54
providers/huawei/config.go
Normal file
54
providers/huawei/config.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package huawei
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/virtual-kubelet/virtual-kubelet/providers"
|
||||
"k8s.io/apimachinery/pkg/util/uuid"
|
||||
)
|
||||
|
||||
type providerConfig struct {
|
||||
Project string
|
||||
Region string
|
||||
Service string
|
||||
OperatingSystem string
|
||||
CPU string
|
||||
Memory string
|
||||
Pods string
|
||||
}
|
||||
|
||||
func (p *CCIProvider) loadConfig(r io.Reader) error {
|
||||
var config providerConfig
|
||||
if _, err := toml.DecodeReader(r, &config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.apiEndpoint = defaultApiEndpoint
|
||||
p.service = "CCI"
|
||||
p.region = config.Region
|
||||
if p.region == "" {
|
||||
p.region = "southchina"
|
||||
}
|
||||
p.cpu = config.CPU
|
||||
if p.cpu == "" {
|
||||
p.cpu = "20"
|
||||
}
|
||||
p.memory = config.Memory
|
||||
if p.memory == "" {
|
||||
p.memory = "100Gi"
|
||||
}
|
||||
p.pods = config.Pods
|
||||
if p.pods == "" {
|
||||
p.pods = "20"
|
||||
}
|
||||
p.project = config.Project
|
||||
if p.project == "" {
|
||||
p.project = string(uuid.NewUUID())
|
||||
}
|
||||
p.operatingSystem = config.OperatingSystem
|
||||
if p.operatingSystem == "" {
|
||||
p.operatingSystem = providers.OperatingSystemLinux
|
||||
}
|
||||
return nil
|
||||
}
|
||||
9
vendor/github.com/pborman/uuid/.travis.yml
generated
vendored
Normal file
9
vendor/github.com/pborman/uuid/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.4.3
|
||||
- 1.5.3
|
||||
- tip
|
||||
|
||||
script:
|
||||
- go test -v ./...
|
||||
10
vendor/github.com/pborman/uuid/CONTRIBUTING.md
generated
vendored
Normal file
10
vendor/github.com/pborman/uuid/CONTRIBUTING.md
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# How to contribute
|
||||
|
||||
We definitely welcome patches and contribution to this project!
|
||||
|
||||
### Legal requirements
|
||||
|
||||
In order to protect both you and ourselves, you will need to sign the
|
||||
[Contributor License Agreement](https://cla.developers.google.com/clas).
|
||||
|
||||
You may have already signed it for other Google projects.
|
||||
1
vendor/github.com/pborman/uuid/CONTRIBUTORS
generated
vendored
Normal file
1
vendor/github.com/pborman/uuid/CONTRIBUTORS
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
Paul Borman <borman@google.com>
|
||||
27
vendor/github.com/pborman/uuid/LICENSE
generated
vendored
Normal file
27
vendor/github.com/pborman/uuid/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2009,2014 Google Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
13
vendor/github.com/pborman/uuid/README.md
generated
vendored
Normal file
13
vendor/github.com/pborman/uuid/README.md
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
This project was automatically exported from code.google.com/p/go-uuid
|
||||
|
||||
# uuid 
|
||||
The uuid package generates and inspects UUIDs based on [RFC 4122](http://tools.ietf.org/html/rfc4122) and DCE 1.1: Authentication and Security Services.
|
||||
|
||||
###### Install
|
||||
`go get github.com/pborman/uuid`
|
||||
|
||||
###### Documentation
|
||||
[](http://godoc.org/github.com/pborman/uuid)
|
||||
|
||||
Full `go doc` style documentation for the package can be viewed online without installing this package by using the GoDoc site here:
|
||||
http://godoc.org/github.com/pborman/uuid
|
||||
84
vendor/github.com/pborman/uuid/dce.go
generated
vendored
Normal file
84
vendor/github.com/pborman/uuid/dce.go
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// A Domain represents a Version 2 domain
|
||||
type Domain byte
|
||||
|
||||
// Domain constants for DCE Security (Version 2) UUIDs.
|
||||
const (
|
||||
Person = Domain(0)
|
||||
Group = Domain(1)
|
||||
Org = Domain(2)
|
||||
)
|
||||
|
||||
// NewDCESecurity returns a DCE Security (Version 2) UUID.
|
||||
//
|
||||
// The domain should be one of Person, Group or Org.
|
||||
// On a POSIX system the id should be the users UID for the Person
|
||||
// domain and the users GID for the Group. The meaning of id for
|
||||
// the domain Org or on non-POSIX systems is site defined.
|
||||
//
|
||||
// For a given domain/id pair the same token may be returned for up to
|
||||
// 7 minutes and 10 seconds.
|
||||
func NewDCESecurity(domain Domain, id uint32) UUID {
|
||||
uuid := NewUUID()
|
||||
if uuid != nil {
|
||||
uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2
|
||||
uuid[9] = byte(domain)
|
||||
binary.BigEndian.PutUint32(uuid[0:], id)
|
||||
}
|
||||
return uuid
|
||||
}
|
||||
|
||||
// NewDCEPerson returns a DCE Security (Version 2) UUID in the person
|
||||
// domain with the id returned by os.Getuid.
|
||||
//
|
||||
// NewDCEPerson(Person, uint32(os.Getuid()))
|
||||
func NewDCEPerson() UUID {
|
||||
return NewDCESecurity(Person, uint32(os.Getuid()))
|
||||
}
|
||||
|
||||
// NewDCEGroup returns a DCE Security (Version 2) UUID in the group
|
||||
// domain with the id returned by os.Getgid.
|
||||
//
|
||||
// NewDCEGroup(Group, uint32(os.Getgid()))
|
||||
func NewDCEGroup() UUID {
|
||||
return NewDCESecurity(Group, uint32(os.Getgid()))
|
||||
}
|
||||
|
||||
// Domain returns the domain for a Version 2 UUID or false.
|
||||
func (uuid UUID) Domain() (Domain, bool) {
|
||||
if v, _ := uuid.Version(); v != 2 {
|
||||
return 0, false
|
||||
}
|
||||
return Domain(uuid[9]), true
|
||||
}
|
||||
|
||||
// Id returns the id for a Version 2 UUID or false.
|
||||
func (uuid UUID) Id() (uint32, bool) {
|
||||
if v, _ := uuid.Version(); v != 2 {
|
||||
return 0, false
|
||||
}
|
||||
return binary.BigEndian.Uint32(uuid[0:4]), true
|
||||
}
|
||||
|
||||
func (d Domain) String() string {
|
||||
switch d {
|
||||
case Person:
|
||||
return "Person"
|
||||
case Group:
|
||||
return "Group"
|
||||
case Org:
|
||||
return "Org"
|
||||
}
|
||||
return fmt.Sprintf("Domain%d", int(d))
|
||||
}
|
||||
8
vendor/github.com/pborman/uuid/doc.go
generated
vendored
Normal file
8
vendor/github.com/pborman/uuid/doc.go
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// The uuid package generates and inspects UUIDs.
|
||||
//
|
||||
// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security Services.
|
||||
package uuid
|
||||
53
vendor/github.com/pborman/uuid/hash.go
generated
vendored
Normal file
53
vendor/github.com/pborman/uuid/hash.go
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"hash"
|
||||
)
|
||||
|
||||
// Well known Name Space IDs and UUIDs
|
||||
var (
|
||||
NameSpace_DNS = Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
|
||||
NameSpace_URL = Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8")
|
||||
NameSpace_OID = Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8")
|
||||
NameSpace_X500 = Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8")
|
||||
NIL = Parse("00000000-0000-0000-0000-000000000000")
|
||||
)
|
||||
|
||||
// NewHash returns a new UUID derived from the hash of space concatenated with
|
||||
// data generated by h. The hash should be at least 16 byte in length. The
|
||||
// first 16 bytes of the hash are used to form the UUID. The version of the
|
||||
// UUID will be the lower 4 bits of version. NewHash is used to implement
|
||||
// NewMD5 and NewSHA1.
|
||||
func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID {
|
||||
h.Reset()
|
||||
h.Write(space)
|
||||
h.Write([]byte(data))
|
||||
s := h.Sum(nil)
|
||||
uuid := make([]byte, 16)
|
||||
copy(uuid, s)
|
||||
uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4)
|
||||
uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant
|
||||
return uuid
|
||||
}
|
||||
|
||||
// NewMD5 returns a new MD5 (Version 3) UUID based on the
|
||||
// supplied name space and data.
|
||||
//
|
||||
// NewHash(md5.New(), space, data, 3)
|
||||
func NewMD5(space UUID, data []byte) UUID {
|
||||
return NewHash(md5.New(), space, data, 3)
|
||||
}
|
||||
|
||||
// NewSHA1 returns a new SHA1 (Version 5) UUID based on the
|
||||
// supplied name space and data.
|
||||
//
|
||||
// NewHash(sha1.New(), space, data, 5)
|
||||
func NewSHA1(space UUID, data []byte) UUID {
|
||||
return NewHash(sha1.New(), space, data, 5)
|
||||
}
|
||||
83
vendor/github.com/pborman/uuid/marshal.go
generated
vendored
Normal file
83
vendor/github.com/pborman/uuid/marshal.go
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright 2016 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// MarshalText implements encoding.TextMarshaler.
|
||||
func (u UUID) MarshalText() ([]byte, error) {
|
||||
if len(u) != 16 {
|
||||
return nil, nil
|
||||
}
|
||||
var js [36]byte
|
||||
encodeHex(js[:], u)
|
||||
return js[:], nil
|
||||
}
|
||||
|
||||
// UnmarshalText implements encoding.TextUnmarshaler.
|
||||
func (u *UUID) UnmarshalText(data []byte) error {
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
id := Parse(string(data))
|
||||
if id == nil {
|
||||
return errors.New("invalid UUID")
|
||||
}
|
||||
*u = id
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary implements encoding.BinaryMarshaler.
|
||||
func (u UUID) MarshalBinary() ([]byte, error) {
|
||||
return u[:], nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
|
||||
func (u *UUID) UnmarshalBinary(data []byte) error {
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
if len(data) != 16 {
|
||||
return fmt.Errorf("invalid UUID (got %d bytes)", len(data))
|
||||
}
|
||||
var id [16]byte
|
||||
copy(id[:], data)
|
||||
*u = id[:]
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalText implements encoding.TextMarshaler.
|
||||
func (u Array) MarshalText() ([]byte, error) {
|
||||
var js [36]byte
|
||||
encodeHex(js[:], u[:])
|
||||
return js[:], nil
|
||||
}
|
||||
|
||||
// UnmarshalText implements encoding.TextUnmarshaler.
|
||||
func (u *Array) UnmarshalText(data []byte) error {
|
||||
id := Parse(string(data))
|
||||
if id == nil {
|
||||
return errors.New("invalid UUID")
|
||||
}
|
||||
*u = id.Array()
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalBinary implements encoding.BinaryMarshaler.
|
||||
func (u Array) MarshalBinary() ([]byte, error) {
|
||||
return u[:], nil
|
||||
}
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
|
||||
func (u *Array) UnmarshalBinary(data []byte) error {
|
||||
if len(data) != 16 {
|
||||
return fmt.Errorf("invalid UUID (got %d bytes)", len(data))
|
||||
}
|
||||
copy(u[:], data)
|
||||
return nil
|
||||
}
|
||||
124
vendor/github.com/pborman/uuid/marshal_test.go
generated
vendored
Normal file
124
vendor/github.com/pborman/uuid/marshal_test.go
generated
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
// Copyright 2014 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var testUUID = Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479")
|
||||
var testArray = testUUID.Array()
|
||||
|
||||
func TestJSON(t *testing.T) {
|
||||
type S struct {
|
||||
ID1 UUID
|
||||
ID2 UUID
|
||||
}
|
||||
s1 := S{ID1: testUUID}
|
||||
data, err := json.Marshal(&s1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var s2 S
|
||||
if err := json.Unmarshal(data, &s2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(&s1, &s2) {
|
||||
t.Errorf("got %#v, want %#v", s2, s1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONArray(t *testing.T) {
|
||||
type S struct {
|
||||
ID1 Array
|
||||
ID2 Array
|
||||
}
|
||||
s1 := S{ID1: testArray}
|
||||
data, err := json.Marshal(&s1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var s2 S
|
||||
if err := json.Unmarshal(data, &s2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(&s1, &s2) {
|
||||
t.Errorf("got %#v, want %#v", s2, s1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshal(t *testing.T) {
|
||||
data, err := testUUID.MarshalBinary()
|
||||
if err != nil {
|
||||
t.Fatalf("MarhsalBinary returned unexpected error %v", err)
|
||||
}
|
||||
if !bytes.Equal(data, testUUID) {
|
||||
t.Fatalf("MarhsalBinary returns %x, want %x", data, testUUID)
|
||||
}
|
||||
var u UUID
|
||||
u.UnmarshalBinary(data)
|
||||
if !Equal(data, u) {
|
||||
t.Fatalf("UnmarhsalBinary returns %v, want %v", u, testUUID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalArray(t *testing.T) {
|
||||
data, err := testArray.MarshalBinary()
|
||||
if err != nil {
|
||||
t.Fatalf("MarhsalBinary returned unexpected error %v", err)
|
||||
}
|
||||
if !bytes.Equal(data, testUUID) {
|
||||
t.Fatalf("MarhsalBinary returns %x, want %x", data, testUUID)
|
||||
}
|
||||
var a Array
|
||||
a.UnmarshalBinary(data)
|
||||
if a != testArray {
|
||||
t.Fatalf("UnmarhsalBinary returns %v, want %v", a, testArray)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalTextArray(t *testing.T) {
|
||||
data, err := testArray.MarshalText()
|
||||
if err != nil {
|
||||
t.Fatalf("MarhsalText returned unexpected error %v", err)
|
||||
}
|
||||
var a Array
|
||||
a.UnmarshalText(data)
|
||||
if a != testArray {
|
||||
t.Fatalf("UnmarhsalText returns %v, want %v", a, testArray)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUUID_MarshalJSON(b *testing.B) {
|
||||
x := &struct {
|
||||
UUID UUID `json:"uuid"`
|
||||
}{}
|
||||
x.UUID = Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479")
|
||||
if x.UUID == nil {
|
||||
b.Fatal("invalid uuid")
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
js, err := json.Marshal(x)
|
||||
if err != nil {
|
||||
b.Fatalf("marshal json: %#v (%v)", js, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUUID_UnmarshalJSON(b *testing.B) {
|
||||
js := []byte(`{"uuid":"f47ac10b-58cc-0372-8567-0e02b2c3d479"}`)
|
||||
var x *struct {
|
||||
UUID UUID `json:"uuid"`
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
err := json.Unmarshal(js, &x)
|
||||
if err != nil {
|
||||
b.Fatalf("marshal json: %#v (%v)", js, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
108
vendor/github.com/pborman/uuid/node.go
generated
vendored
Normal file
108
vendor/github.com/pborman/uuid/node.go
generated
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
nodeMu sync.Mutex
|
||||
ifname string // name of interface being used
|
||||
nodeID []byte // hardware for version 1 UUIDs
|
||||
)
|
||||
|
||||
// NodeInterface returns the name of the interface from which the NodeID was
|
||||
// derived. The interface "user" is returned if the NodeID was set by
|
||||
// SetNodeID.
|
||||
func NodeInterface() string {
|
||||
defer nodeMu.Unlock()
|
||||
nodeMu.Lock()
|
||||
return ifname
|
||||
}
|
||||
|
||||
// SetNodeInterface selects the hardware address to be used for Version 1 UUIDs.
|
||||
// If name is "" then the first usable interface found will be used or a random
|
||||
// Node ID will be generated. If a named interface cannot be found then false
|
||||
// is returned.
|
||||
//
|
||||
// SetNodeInterface never fails when name is "".
|
||||
func SetNodeInterface(name string) bool {
|
||||
defer nodeMu.Unlock()
|
||||
nodeMu.Lock()
|
||||
if nodeID != nil {
|
||||
return true
|
||||
}
|
||||
return setNodeInterface(name)
|
||||
}
|
||||
|
||||
func setNodeInterface(name string) bool {
|
||||
|
||||
iname, addr := getHardwareInterface(name) // null implementation for js
|
||||
if iname != "" && setNodeID(addr) {
|
||||
ifname = iname
|
||||
return true
|
||||
}
|
||||
|
||||
// We found no interfaces with a valid hardware address. If name
|
||||
// does not specify a specific interface generate a random Node ID
|
||||
// (section 4.1.6)
|
||||
if name == "" {
|
||||
if nodeID == nil {
|
||||
nodeID = make([]byte, 6)
|
||||
}
|
||||
randomBits(nodeID)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// NodeID returns a slice of a copy of the current Node ID, setting the Node ID
|
||||
// if not already set.
|
||||
func NodeID() []byte {
|
||||
defer nodeMu.Unlock()
|
||||
nodeMu.Lock()
|
||||
if nodeID == nil {
|
||||
setNodeInterface("")
|
||||
}
|
||||
nid := make([]byte, 6)
|
||||
copy(nid, nodeID)
|
||||
return nid
|
||||
}
|
||||
|
||||
// SetNodeID sets the Node ID to be used for Version 1 UUIDs. The first 6 bytes
|
||||
// of id are used. If id is less than 6 bytes then false is returned and the
|
||||
// Node ID is not set.
|
||||
func SetNodeID(id []byte) bool {
|
||||
defer nodeMu.Unlock()
|
||||
nodeMu.Lock()
|
||||
if setNodeID(id) {
|
||||
ifname = "user"
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func setNodeID(id []byte) bool {
|
||||
if len(id) < 6 {
|
||||
return false
|
||||
}
|
||||
if nodeID == nil {
|
||||
nodeID = make([]byte, 6)
|
||||
}
|
||||
copy(nodeID, id)
|
||||
return true
|
||||
}
|
||||
|
||||
// NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is
|
||||
// not valid. The NodeID is only well defined for version 1 and 2 UUIDs.
|
||||
func (uuid UUID) NodeID() []byte {
|
||||
if len(uuid) != 16 {
|
||||
return nil
|
||||
}
|
||||
node := make([]byte, 6)
|
||||
copy(node, uuid[10:])
|
||||
return node
|
||||
}
|
||||
12
vendor/github.com/pborman/uuid/node_js.go
generated
vendored
Normal file
12
vendor/github.com/pborman/uuid/node_js.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2017 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build js
|
||||
|
||||
package uuid
|
||||
|
||||
// getHardwareInterface returns nil values for the JS version of the code.
|
||||
// This remvoves the "net" dependency, because it is not used in the browser.
|
||||
// Using the "net" library inflates the size of the transpiled JS code by 673k bytes.
|
||||
func getHardwareInterface(name string) (string, []byte) { return "", nil }
|
||||
36
vendor/github.com/pborman/uuid/node_net.go
generated
vendored
Normal file
36
vendor/github.com/pborman/uuid/node_net.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright 2017 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !js
|
||||
|
||||
package uuid
|
||||
|
||||
import "net"
|
||||
|
||||
var interfaces []net.Interface // cached list of interfaces
|
||||
|
||||
// getHardwareInterface returns the name and hardware address of interface name.
|
||||
// If name is "" then the name and hardware address of one of the system's
|
||||
// interfaces is returned. If no interfaces are found (name does not exist or
|
||||
// there are no interfaces) then "", nil is returned.
|
||||
//
|
||||
// Only addresses of at least 6 bytes are returned.
|
||||
func getHardwareInterface(name string) (string, []byte) {
|
||||
if interfaces == nil {
|
||||
var err error
|
||||
interfaces, err = net.Interfaces()
|
||||
if err != nil {
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
for _, ifs := range interfaces {
|
||||
if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) {
|
||||
if setNodeID(ifs.HardwareAddr) {
|
||||
ifname = ifs.Name
|
||||
return ifname, nodeID
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
66
vendor/github.com/pborman/uuid/seq_test.go
generated
vendored
Normal file
66
vendor/github.com/pborman/uuid/seq_test.go
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
// Copyright 2014 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"runtime"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// This test is only run when --regressions is passed on the go test line.
|
||||
var regressions = flag.Bool("regressions", false, "run uuid regression tests")
|
||||
|
||||
// TestClockSeqRace tests for a particular race condition of returning two
|
||||
// identical Version1 UUIDs. The duration of 1 minute was chosen as the race
|
||||
// condition, before being fixed, nearly always occured in under 30 seconds.
|
||||
func TestClockSeqRace(t *testing.T) {
|
||||
if !*regressions {
|
||||
t.Skip("skipping regression tests")
|
||||
}
|
||||
duration := time.Minute
|
||||
|
||||
done := make(chan struct{})
|
||||
defer close(done)
|
||||
|
||||
ch := make(chan UUID, 10000)
|
||||
ncpu := runtime.NumCPU()
|
||||
switch ncpu {
|
||||
case 0, 1:
|
||||
// We can't run the test effectively.
|
||||
t.Skip("skipping race test, only one CPU detected")
|
||||
return
|
||||
default:
|
||||
runtime.GOMAXPROCS(ncpu)
|
||||
}
|
||||
for i := 0; i < ncpu; i++ {
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
case ch <- NewUUID():
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
uuids := make(map[string]bool)
|
||||
cnt := 0
|
||||
start := time.Now()
|
||||
for u := range ch {
|
||||
s := u.String()
|
||||
if uuids[s] {
|
||||
t.Errorf("duplicate uuid after %d in %v: %s", cnt, time.Since(start), s)
|
||||
return
|
||||
}
|
||||
uuids[s] = true
|
||||
if time.Since(start) > duration {
|
||||
return
|
||||
}
|
||||
cnt++
|
||||
}
|
||||
}
|
||||
66
vendor/github.com/pborman/uuid/sql.go
generated
vendored
Normal file
66
vendor/github.com/pborman/uuid/sql.go
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
// Copyright 2015 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Scan implements sql.Scanner so UUIDs can be read from databases transparently
|
||||
// Currently, database types that map to string and []byte are supported. Please
|
||||
// consult database-specific driver documentation for matching types.
|
||||
func (uuid *UUID) Scan(src interface{}) error {
|
||||
switch src.(type) {
|
||||
case string:
|
||||
// if an empty UUID comes from a table, we return a null UUID
|
||||
if src.(string) == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// see uuid.Parse for required string format
|
||||
parsed := Parse(src.(string))
|
||||
|
||||
if parsed == nil {
|
||||
return errors.New("Scan: invalid UUID format")
|
||||
}
|
||||
|
||||
*uuid = parsed
|
||||
case []byte:
|
||||
b := src.([]byte)
|
||||
|
||||
// if an empty UUID comes from a table, we return a null UUID
|
||||
if len(b) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// assumes a simple slice of bytes if 16 bytes
|
||||
// otherwise attempts to parse
|
||||
if len(b) == 16 {
|
||||
*uuid = UUID(b)
|
||||
} else {
|
||||
u := Parse(string(b))
|
||||
|
||||
if u == nil {
|
||||
return errors.New("Scan: invalid UUID format")
|
||||
}
|
||||
|
||||
*uuid = u
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("Scan: unable to scan type %T into UUID", src)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Value implements sql.Valuer so that UUIDs can be written to databases
|
||||
// transparently. Currently, UUIDs map to strings. Please consult
|
||||
// database-specific driver documentation for matching types.
|
||||
func (uuid UUID) Value() (driver.Value, error) {
|
||||
return uuid.String(), nil
|
||||
}
|
||||
96
vendor/github.com/pborman/uuid/sql_test.go
generated
vendored
Normal file
96
vendor/github.com/pborman/uuid/sql_test.go
generated
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
// Copyright 2015 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestScan(t *testing.T) {
|
||||
var stringTest string = "f47ac10b-58cc-0372-8567-0e02b2c3d479"
|
||||
var byteTest []byte = Parse(stringTest)
|
||||
var badTypeTest int = 6
|
||||
var invalidTest string = "f47ac10b-58cc-0372-8567-0e02b2c3d4"
|
||||
|
||||
// sunny day tests
|
||||
|
||||
var uuid UUID
|
||||
err := (&uuid).Scan(stringTest)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = (&uuid).Scan([]byte(stringTest))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err = (&uuid).Scan(byteTest)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// bad type tests
|
||||
|
||||
err = (&uuid).Scan(badTypeTest)
|
||||
if err == nil {
|
||||
t.Error("int correctly parsed and shouldn't have")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "unable to scan type") {
|
||||
t.Error("attempting to parse an int returned an incorrect error message")
|
||||
}
|
||||
|
||||
// invalid/incomplete uuids
|
||||
|
||||
err = (&uuid).Scan(invalidTest)
|
||||
if err == nil {
|
||||
t.Error("invalid uuid was parsed without error")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "invalid UUID") {
|
||||
t.Error("attempting to parse an invalid UUID returned an incorrect error message")
|
||||
}
|
||||
|
||||
err = (&uuid).Scan(byteTest[:len(byteTest)-2])
|
||||
if err == nil {
|
||||
t.Error("invalid byte uuid was parsed without error")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "invalid UUID") {
|
||||
t.Error("attempting to parse an invalid byte UUID returned an incorrect error message")
|
||||
}
|
||||
|
||||
// empty tests
|
||||
|
||||
uuid = nil
|
||||
var emptySlice []byte
|
||||
err = (&uuid).Scan(emptySlice)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if uuid != nil {
|
||||
t.Error("UUID was not nil after scanning empty byte slice")
|
||||
}
|
||||
|
||||
uuid = nil
|
||||
var emptyString string
|
||||
err = (&uuid).Scan(emptyString)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if uuid != nil {
|
||||
t.Error("UUID was not nil after scanning empty string")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValue(t *testing.T) {
|
||||
stringTest := "f47ac10b-58cc-0372-8567-0e02b2c3d479"
|
||||
uuid := Parse(stringTest)
|
||||
val, _ := uuid.Value()
|
||||
if val != stringTest {
|
||||
t.Error("Value() did not return expected string")
|
||||
}
|
||||
}
|
||||
132
vendor/github.com/pborman/uuid/time.go
generated
vendored
Normal file
132
vendor/github.com/pborman/uuid/time.go
generated
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
// Copyright 2014 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// A Time represents a time as the number of 100's of nanoseconds since 15 Oct
|
||||
// 1582.
|
||||
type Time int64
|
||||
|
||||
const (
|
||||
lillian = 2299160 // Julian day of 15 Oct 1582
|
||||
unix = 2440587 // Julian day of 1 Jan 1970
|
||||
epoch = unix - lillian // Days between epochs
|
||||
g1582 = epoch * 86400 // seconds between epochs
|
||||
g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs
|
||||
)
|
||||
|
||||
var (
|
||||
timeMu sync.Mutex
|
||||
lasttime uint64 // last time we returned
|
||||
clock_seq uint16 // clock sequence for this run
|
||||
|
||||
timeNow = time.Now // for testing
|
||||
)
|
||||
|
||||
// UnixTime converts t the number of seconds and nanoseconds using the Unix
|
||||
// epoch of 1 Jan 1970.
|
||||
func (t Time) UnixTime() (sec, nsec int64) {
|
||||
sec = int64(t - g1582ns100)
|
||||
nsec = (sec % 10000000) * 100
|
||||
sec /= 10000000
|
||||
return sec, nsec
|
||||
}
|
||||
|
||||
// GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and
|
||||
// clock sequence as well as adjusting the clock sequence as needed. An error
|
||||
// is returned if the current time cannot be determined.
|
||||
func GetTime() (Time, uint16, error) {
|
||||
defer timeMu.Unlock()
|
||||
timeMu.Lock()
|
||||
return getTime()
|
||||
}
|
||||
|
||||
func getTime() (Time, uint16, error) {
|
||||
t := timeNow()
|
||||
|
||||
// If we don't have a clock sequence already, set one.
|
||||
if clock_seq == 0 {
|
||||
setClockSequence(-1)
|
||||
}
|
||||
now := uint64(t.UnixNano()/100) + g1582ns100
|
||||
|
||||
// If time has gone backwards with this clock sequence then we
|
||||
// increment the clock sequence
|
||||
if now <= lasttime {
|
||||
clock_seq = ((clock_seq + 1) & 0x3fff) | 0x8000
|
||||
}
|
||||
lasttime = now
|
||||
return Time(now), clock_seq, nil
|
||||
}
|
||||
|
||||
// ClockSequence returns the current clock sequence, generating one if not
|
||||
// already set. The clock sequence is only used for Version 1 UUIDs.
|
||||
//
|
||||
// The uuid package does not use global static storage for the clock sequence or
|
||||
// the last time a UUID was generated. Unless SetClockSequence a new random
|
||||
// clock sequence is generated the first time a clock sequence is requested by
|
||||
// ClockSequence, GetTime, or NewUUID. (section 4.2.1.1) sequence is generated
|
||||
// for
|
||||
func ClockSequence() int {
|
||||
defer timeMu.Unlock()
|
||||
timeMu.Lock()
|
||||
return clockSequence()
|
||||
}
|
||||
|
||||
func clockSequence() int {
|
||||
if clock_seq == 0 {
|
||||
setClockSequence(-1)
|
||||
}
|
||||
return int(clock_seq & 0x3fff)
|
||||
}
|
||||
|
||||
// SetClockSeq sets the clock sequence to the lower 14 bits of seq. Setting to
|
||||
// -1 causes a new sequence to be generated.
|
||||
func SetClockSequence(seq int) {
|
||||
defer timeMu.Unlock()
|
||||
timeMu.Lock()
|
||||
setClockSequence(seq)
|
||||
}
|
||||
|
||||
func setClockSequence(seq int) {
|
||||
if seq == -1 {
|
||||
var b [2]byte
|
||||
randomBits(b[:]) // clock sequence
|
||||
seq = int(b[0])<<8 | int(b[1])
|
||||
}
|
||||
old_seq := clock_seq
|
||||
clock_seq = uint16(seq&0x3fff) | 0x8000 // Set our variant
|
||||
if old_seq != clock_seq {
|
||||
lasttime = 0
|
||||
}
|
||||
}
|
||||
|
||||
// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in
|
||||
// uuid. It returns false if uuid is not valid. The time is only well defined
|
||||
// for version 1 and 2 UUIDs.
|
||||
func (uuid UUID) Time() (Time, bool) {
|
||||
if len(uuid) != 16 {
|
||||
return 0, false
|
||||
}
|
||||
time := int64(binary.BigEndian.Uint32(uuid[0:4]))
|
||||
time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32
|
||||
time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48
|
||||
return Time(time), true
|
||||
}
|
||||
|
||||
// ClockSequence returns the clock sequence encoded in uuid. It returns false
|
||||
// if uuid is not valid. The clock sequence is only well defined for version 1
|
||||
// and 2 UUIDs.
|
||||
func (uuid UUID) ClockSequence() (int, bool) {
|
||||
if len(uuid) != 16 {
|
||||
return 0, false
|
||||
}
|
||||
return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff, true
|
||||
}
|
||||
43
vendor/github.com/pborman/uuid/util.go
generated
vendored
Normal file
43
vendor/github.com/pborman/uuid/util.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// randomBits completely fills slice b with random data.
|
||||
func randomBits(b []byte) {
|
||||
if _, err := io.ReadFull(rander, b); err != nil {
|
||||
panic(err.Error()) // rand should never fail
|
||||
}
|
||||
}
|
||||
|
||||
// xvalues returns the value of a byte as a hexadecimal digit or 255.
|
||||
var xvalues = [256]byte{
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255,
|
||||
255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
}
|
||||
|
||||
// xtob converts the the first two hex bytes of x into a byte.
|
||||
func xtob(x string) (byte, bool) {
|
||||
b1 := xvalues[x[0]]
|
||||
b2 := xvalues[x[1]]
|
||||
return (b1 << 4) | b2, b1 != 255 && b2 != 255
|
||||
}
|
||||
201
vendor/github.com/pborman/uuid/uuid.go
generated
vendored
Normal file
201
vendor/github.com/pborman/uuid/uuid.go
generated
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Array is a pass-by-value UUID that can be used as an effecient key in a map.
|
||||
type Array [16]byte
|
||||
|
||||
// UUID converts uuid into a slice.
|
||||
func (uuid Array) UUID() UUID {
|
||||
return uuid[:]
|
||||
}
|
||||
|
||||
// String returns the string representation of uuid,
|
||||
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
|
||||
func (uuid Array) String() string {
|
||||
return uuid.UUID().String()
|
||||
}
|
||||
|
||||
// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC
|
||||
// 4122.
|
||||
type UUID []byte
|
||||
|
||||
// A Version represents a UUIDs version.
|
||||
type Version byte
|
||||
|
||||
// A Variant represents a UUIDs variant.
|
||||
type Variant byte
|
||||
|
||||
// Constants returned by Variant.
|
||||
const (
|
||||
Invalid = Variant(iota) // Invalid UUID
|
||||
RFC4122 // The variant specified in RFC4122
|
||||
Reserved // Reserved, NCS backward compatibility.
|
||||
Microsoft // Reserved, Microsoft Corporation backward compatibility.
|
||||
Future // Reserved for future definition.
|
||||
)
|
||||
|
||||
var rander = rand.Reader // random function
|
||||
|
||||
// New returns a new random (version 4) UUID as a string. It is a convenience
|
||||
// function for NewRandom().String().
|
||||
func New() string {
|
||||
return NewRandom().String()
|
||||
}
|
||||
|
||||
// Parse decodes s into a UUID or returns nil. Both the UUID form of
|
||||
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
|
||||
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded.
|
||||
func Parse(s string) UUID {
|
||||
if len(s) == 36+9 {
|
||||
if strings.ToLower(s[:9]) != "urn:uuid:" {
|
||||
return nil
|
||||
}
|
||||
s = s[9:]
|
||||
} else if len(s) != 36 {
|
||||
return nil
|
||||
}
|
||||
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
|
||||
return nil
|
||||
}
|
||||
var uuid [16]byte
|
||||
for i, x := range [16]int{
|
||||
0, 2, 4, 6,
|
||||
9, 11,
|
||||
14, 16,
|
||||
19, 21,
|
||||
24, 26, 28, 30, 32, 34} {
|
||||
if v, ok := xtob(s[x:]); !ok {
|
||||
return nil
|
||||
} else {
|
||||
uuid[i] = v
|
||||
}
|
||||
}
|
||||
return uuid[:]
|
||||
}
|
||||
|
||||
// Equal returns true if uuid1 and uuid2 are equal.
|
||||
func Equal(uuid1, uuid2 UUID) bool {
|
||||
return bytes.Equal(uuid1, uuid2)
|
||||
}
|
||||
|
||||
// Array returns an array representation of uuid that can be used as a map key.
|
||||
// Array panics if uuid is not valid.
|
||||
func (uuid UUID) Array() Array {
|
||||
if len(uuid) != 16 {
|
||||
panic("invalid uuid")
|
||||
}
|
||||
var a Array
|
||||
copy(a[:], uuid)
|
||||
return a
|
||||
}
|
||||
|
||||
// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
||||
// , or "" if uuid is invalid.
|
||||
func (uuid UUID) String() string {
|
||||
if len(uuid) != 16 {
|
||||
return ""
|
||||
}
|
||||
var buf [36]byte
|
||||
encodeHex(buf[:], uuid)
|
||||
return string(buf[:])
|
||||
}
|
||||
|
||||
// URN returns the RFC 2141 URN form of uuid,
|
||||
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, or "" if uuid is invalid.
|
||||
func (uuid UUID) URN() string {
|
||||
if len(uuid) != 16 {
|
||||
return ""
|
||||
}
|
||||
var buf [36 + 9]byte
|
||||
copy(buf[:], "urn:uuid:")
|
||||
encodeHex(buf[9:], uuid)
|
||||
return string(buf[:])
|
||||
}
|
||||
|
||||
func encodeHex(dst []byte, uuid UUID) {
|
||||
hex.Encode(dst[:], uuid[:4])
|
||||
dst[8] = '-'
|
||||
hex.Encode(dst[9:13], uuid[4:6])
|
||||
dst[13] = '-'
|
||||
hex.Encode(dst[14:18], uuid[6:8])
|
||||
dst[18] = '-'
|
||||
hex.Encode(dst[19:23], uuid[8:10])
|
||||
dst[23] = '-'
|
||||
hex.Encode(dst[24:], uuid[10:])
|
||||
}
|
||||
|
||||
// Variant returns the variant encoded in uuid. It returns Invalid if
|
||||
// uuid is invalid.
|
||||
func (uuid UUID) Variant() Variant {
|
||||
if len(uuid) != 16 {
|
||||
return Invalid
|
||||
}
|
||||
switch {
|
||||
case (uuid[8] & 0xc0) == 0x80:
|
||||
return RFC4122
|
||||
case (uuid[8] & 0xe0) == 0xc0:
|
||||
return Microsoft
|
||||
case (uuid[8] & 0xe0) == 0xe0:
|
||||
return Future
|
||||
default:
|
||||
return Reserved
|
||||
}
|
||||
}
|
||||
|
||||
// Version returns the version of uuid. It returns false if uuid is not
|
||||
// valid.
|
||||
func (uuid UUID) Version() (Version, bool) {
|
||||
if len(uuid) != 16 {
|
||||
return 0, false
|
||||
}
|
||||
return Version(uuid[6] >> 4), true
|
||||
}
|
||||
|
||||
func (v Version) String() string {
|
||||
if v > 15 {
|
||||
return fmt.Sprintf("BAD_VERSION_%d", v)
|
||||
}
|
||||
return fmt.Sprintf("VERSION_%d", v)
|
||||
}
|
||||
|
||||
func (v Variant) String() string {
|
||||
switch v {
|
||||
case RFC4122:
|
||||
return "RFC4122"
|
||||
case Reserved:
|
||||
return "Reserved"
|
||||
case Microsoft:
|
||||
return "Microsoft"
|
||||
case Future:
|
||||
return "Future"
|
||||
case Invalid:
|
||||
return "Invalid"
|
||||
}
|
||||
return fmt.Sprintf("BadVariant%d", int(v))
|
||||
}
|
||||
|
||||
// SetRand sets the random number generator to r, which implements io.Reader.
|
||||
// If r.Read returns an error when the package requests random data then
|
||||
// a panic will be issued.
|
||||
//
|
||||
// Calling SetRand with nil sets the random number generator to the default
|
||||
// generator.
|
||||
func SetRand(r io.Reader) {
|
||||
if r == nil {
|
||||
rander = rand.Reader
|
||||
return
|
||||
}
|
||||
rander = r
|
||||
}
|
||||
552
vendor/github.com/pborman/uuid/uuid_test.go
generated
vendored
Normal file
552
vendor/github.com/pborman/uuid/uuid_test.go
generated
vendored
Normal file
@@ -0,0 +1,552 @@
|
||||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type test struct {
|
||||
in string
|
||||
version Version
|
||||
variant Variant
|
||||
isuuid bool
|
||||
}
|
||||
|
||||
var tests = []test{
|
||||
{"f47ac10b-58cc-0372-8567-0e02b2c3d479", 0, RFC4122, true},
|
||||
{"f47ac10b-58cc-1372-8567-0e02b2c3d479", 1, RFC4122, true},
|
||||
{"f47ac10b-58cc-2372-8567-0e02b2c3d479", 2, RFC4122, true},
|
||||
{"f47ac10b-58cc-3372-8567-0e02b2c3d479", 3, RFC4122, true},
|
||||
{"f47ac10b-58cc-4372-8567-0e02b2c3d479", 4, RFC4122, true},
|
||||
{"f47ac10b-58cc-5372-8567-0e02b2c3d479", 5, RFC4122, true},
|
||||
{"f47ac10b-58cc-6372-8567-0e02b2c3d479", 6, RFC4122, true},
|
||||
{"f47ac10b-58cc-7372-8567-0e02b2c3d479", 7, RFC4122, true},
|
||||
{"f47ac10b-58cc-8372-8567-0e02b2c3d479", 8, RFC4122, true},
|
||||
{"f47ac10b-58cc-9372-8567-0e02b2c3d479", 9, RFC4122, true},
|
||||
{"f47ac10b-58cc-a372-8567-0e02b2c3d479", 10, RFC4122, true},
|
||||
{"f47ac10b-58cc-b372-8567-0e02b2c3d479", 11, RFC4122, true},
|
||||
{"f47ac10b-58cc-c372-8567-0e02b2c3d479", 12, RFC4122, true},
|
||||
{"f47ac10b-58cc-d372-8567-0e02b2c3d479", 13, RFC4122, true},
|
||||
{"f47ac10b-58cc-e372-8567-0e02b2c3d479", 14, RFC4122, true},
|
||||
{"f47ac10b-58cc-f372-8567-0e02b2c3d479", 15, RFC4122, true},
|
||||
|
||||
{"urn:uuid:f47ac10b-58cc-4372-0567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"URN:UUID:f47ac10b-58cc-4372-0567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-0567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-1567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-2567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-3567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-4567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-5567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-6567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-7567-0e02b2c3d479", 4, Reserved, true},
|
||||
{"f47ac10b-58cc-4372-8567-0e02b2c3d479", 4, RFC4122, true},
|
||||
{"f47ac10b-58cc-4372-9567-0e02b2c3d479", 4, RFC4122, true},
|
||||
{"f47ac10b-58cc-4372-a567-0e02b2c3d479", 4, RFC4122, true},
|
||||
{"f47ac10b-58cc-4372-b567-0e02b2c3d479", 4, RFC4122, true},
|
||||
{"f47ac10b-58cc-4372-c567-0e02b2c3d479", 4, Microsoft, true},
|
||||
{"f47ac10b-58cc-4372-d567-0e02b2c3d479", 4, Microsoft, true},
|
||||
{"f47ac10b-58cc-4372-e567-0e02b2c3d479", 4, Future, true},
|
||||
{"f47ac10b-58cc-4372-f567-0e02b2c3d479", 4, Future, true},
|
||||
|
||||
{"f47ac10b158cc-5372-a567-0e02b2c3d479", 0, Invalid, false},
|
||||
{"f47ac10b-58cc25372-a567-0e02b2c3d479", 0, Invalid, false},
|
||||
{"f47ac10b-58cc-53723a567-0e02b2c3d479", 0, Invalid, false},
|
||||
{"f47ac10b-58cc-5372-a56740e02b2c3d479", 0, Invalid, false},
|
||||
{"f47ac10b-58cc-5372-a567-0e02-2c3d479", 0, Invalid, false},
|
||||
{"g47ac10b-58cc-4372-a567-0e02b2c3d479", 0, Invalid, false},
|
||||
}
|
||||
|
||||
var constants = []struct {
|
||||
c interface{}
|
||||
name string
|
||||
}{
|
||||
{Person, "Person"},
|
||||
{Group, "Group"},
|
||||
{Org, "Org"},
|
||||
{Invalid, "Invalid"},
|
||||
{RFC4122, "RFC4122"},
|
||||
{Reserved, "Reserved"},
|
||||
{Microsoft, "Microsoft"},
|
||||
{Future, "Future"},
|
||||
{Domain(17), "Domain17"},
|
||||
{Variant(42), "BadVariant42"},
|
||||
}
|
||||
|
||||
func testTest(t *testing.T, in string, tt test) {
|
||||
uuid := Parse(in)
|
||||
if ok := (uuid != nil); ok != tt.isuuid {
|
||||
t.Errorf("Parse(%s) got %v expected %v\b", in, ok, tt.isuuid)
|
||||
}
|
||||
if uuid == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if v := uuid.Variant(); v != tt.variant {
|
||||
t.Errorf("Variant(%s) got %d expected %d\b", in, v, tt.variant)
|
||||
}
|
||||
if v, _ := uuid.Version(); v != tt.version {
|
||||
t.Errorf("Version(%s) got %d expected %d\b", in, v, tt.version)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUUID(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
testTest(t, tt.in, tt)
|
||||
testTest(t, strings.ToUpper(tt.in), tt)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConstants(t *testing.T) {
|
||||
for x, tt := range constants {
|
||||
v, ok := tt.c.(fmt.Stringer)
|
||||
if !ok {
|
||||
t.Errorf("%x: %v: not a stringer", x, v)
|
||||
} else if s := v.String(); s != tt.name {
|
||||
v, _ := tt.c.(int)
|
||||
t.Errorf("%x: Constant %T:%d gives %q, expected %q", x, tt.c, v, s, tt.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomUUID(t *testing.T) {
|
||||
m := make(map[string]bool)
|
||||
for x := 1; x < 32; x++ {
|
||||
uuid := NewRandom()
|
||||
s := uuid.String()
|
||||
if m[s] {
|
||||
t.Errorf("NewRandom returned duplicated UUID %s", s)
|
||||
}
|
||||
m[s] = true
|
||||
if v, _ := uuid.Version(); v != 4 {
|
||||
t.Errorf("Random UUID of version %s", v)
|
||||
}
|
||||
if uuid.Variant() != RFC4122 {
|
||||
t.Errorf("Random UUID is variant %d", uuid.Variant())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
m := make(map[string]bool)
|
||||
for x := 1; x < 32; x++ {
|
||||
s := New()
|
||||
if m[s] {
|
||||
t.Errorf("New returned duplicated UUID %s", s)
|
||||
}
|
||||
m[s] = true
|
||||
uuid := Parse(s)
|
||||
if uuid == nil {
|
||||
t.Errorf("New returned %q which does not decode", s)
|
||||
continue
|
||||
}
|
||||
if v, _ := uuid.Version(); v != 4 {
|
||||
t.Errorf("Random UUID of version %s", v)
|
||||
}
|
||||
if uuid.Variant() != RFC4122 {
|
||||
t.Errorf("Random UUID is variant %d", uuid.Variant())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func clockSeq(t *testing.T, uuid UUID) int {
|
||||
seq, ok := uuid.ClockSequence()
|
||||
if !ok {
|
||||
t.Fatalf("%s: invalid clock sequence", uuid)
|
||||
}
|
||||
return seq
|
||||
}
|
||||
|
||||
func TestClockSeq(t *testing.T) {
|
||||
// Fake time.Now for this test to return a monotonically advancing time; restore it at end.
|
||||
defer func(orig func() time.Time) { timeNow = orig }(timeNow)
|
||||
monTime := time.Now()
|
||||
timeNow = func() time.Time {
|
||||
monTime = monTime.Add(1 * time.Second)
|
||||
return monTime
|
||||
}
|
||||
|
||||
SetClockSequence(-1)
|
||||
uuid1 := NewUUID()
|
||||
uuid2 := NewUUID()
|
||||
|
||||
if clockSeq(t, uuid1) != clockSeq(t, uuid2) {
|
||||
t.Errorf("clock sequence %d != %d", clockSeq(t, uuid1), clockSeq(t, uuid2))
|
||||
}
|
||||
|
||||
SetClockSequence(-1)
|
||||
uuid2 = NewUUID()
|
||||
|
||||
// Just on the very off chance we generated the same sequence
|
||||
// two times we try again.
|
||||
if clockSeq(t, uuid1) == clockSeq(t, uuid2) {
|
||||
SetClockSequence(-1)
|
||||
uuid2 = NewUUID()
|
||||
}
|
||||
if clockSeq(t, uuid1) == clockSeq(t, uuid2) {
|
||||
t.Errorf("Duplicate clock sequence %d", clockSeq(t, uuid1))
|
||||
}
|
||||
|
||||
SetClockSequence(0x1234)
|
||||
uuid1 = NewUUID()
|
||||
if seq := clockSeq(t, uuid1); seq != 0x1234 {
|
||||
t.Errorf("%s: expected seq 0x1234 got 0x%04x", uuid1, seq)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCoding(t *testing.T) {
|
||||
text := "7d444840-9dc0-11d1-b245-5ffdce74fad2"
|
||||
urn := "urn:uuid:7d444840-9dc0-11d1-b245-5ffdce74fad2"
|
||||
data := UUID{
|
||||
0x7d, 0x44, 0x48, 0x40,
|
||||
0x9d, 0xc0,
|
||||
0x11, 0xd1,
|
||||
0xb2, 0x45,
|
||||
0x5f, 0xfd, 0xce, 0x74, 0xfa, 0xd2,
|
||||
}
|
||||
if v := data.String(); v != text {
|
||||
t.Errorf("%x: encoded to %s, expected %s", data, v, text)
|
||||
}
|
||||
if v := data.URN(); v != urn {
|
||||
t.Errorf("%x: urn is %s, expected %s", data, v, urn)
|
||||
}
|
||||
|
||||
uuid := Parse(text)
|
||||
if !Equal(uuid, data) {
|
||||
t.Errorf("%s: decoded to %s, expected %s", text, uuid, data)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersion1(t *testing.T) {
|
||||
uuid1 := NewUUID()
|
||||
uuid2 := NewUUID()
|
||||
|
||||
if Equal(uuid1, uuid2) {
|
||||
t.Errorf("%s:duplicate uuid", uuid1)
|
||||
}
|
||||
if v, _ := uuid1.Version(); v != 1 {
|
||||
t.Errorf("%s: version %s expected 1", uuid1, v)
|
||||
}
|
||||
if v, _ := uuid2.Version(); v != 1 {
|
||||
t.Errorf("%s: version %s expected 1", uuid2, v)
|
||||
}
|
||||
n1 := uuid1.NodeID()
|
||||
n2 := uuid2.NodeID()
|
||||
if !bytes.Equal(n1, n2) {
|
||||
t.Errorf("Different nodes %x != %x", n1, n2)
|
||||
}
|
||||
t1, ok := uuid1.Time()
|
||||
if !ok {
|
||||
t.Errorf("%s: invalid time", uuid1)
|
||||
}
|
||||
t2, ok := uuid2.Time()
|
||||
if !ok {
|
||||
t.Errorf("%s: invalid time", uuid2)
|
||||
}
|
||||
q1, ok := uuid1.ClockSequence()
|
||||
if !ok {
|
||||
t.Errorf("%s: invalid clock sequence", uuid1)
|
||||
}
|
||||
q2, ok := uuid2.ClockSequence()
|
||||
if !ok {
|
||||
t.Errorf("%s: invalid clock sequence", uuid2)
|
||||
}
|
||||
|
||||
switch {
|
||||
case t1 == t2 && q1 == q2:
|
||||
t.Error("time stopped")
|
||||
case t1 > t2 && q1 == q2:
|
||||
t.Error("time reversed")
|
||||
case t1 < t2 && q1 != q2:
|
||||
t.Error("clock sequence chaned unexpectedly")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNode(t *testing.T) {
|
||||
// This test is mostly to make sure we don't leave nodeMu locked.
|
||||
ifname = ""
|
||||
if ni := NodeInterface(); ni != "" {
|
||||
t.Errorf("NodeInterface got %q, want %q", ni, "")
|
||||
}
|
||||
nodeID = nil // Reset global state for next test
|
||||
if SetNodeInterface("xyzzy") {
|
||||
t.Error("SetNodeInterface succeeded on a bad interface name")
|
||||
}
|
||||
nodeID = nil // Reset global state for next test
|
||||
if !SetNodeInterface("") {
|
||||
t.Error("SetNodeInterface failed")
|
||||
}
|
||||
|
||||
if runtime.GOARCH != "js" {
|
||||
if ni := NodeInterface(); ni == "" {
|
||||
t.Error("NodeInterface returned an empty string")
|
||||
}
|
||||
}
|
||||
|
||||
ni := NodeID()
|
||||
if len(ni) != 6 {
|
||||
t.Errorf("ni got %d bytes, want 6", len(ni))
|
||||
}
|
||||
hasData := false
|
||||
for _, b := range ni {
|
||||
if b != 0 {
|
||||
hasData = true
|
||||
}
|
||||
}
|
||||
if !hasData {
|
||||
t.Error("nodeid is all zeros")
|
||||
}
|
||||
|
||||
id := []byte{1, 2, 3, 4, 5, 6, 7, 8}
|
||||
SetNodeID(id)
|
||||
ni = NodeID()
|
||||
if !bytes.Equal(ni, id[:6]) {
|
||||
t.Errorf("got nodeid %v, want %v", ni, id[:6])
|
||||
}
|
||||
|
||||
if ni := NodeInterface(); ni != "user" {
|
||||
t.Errorf("got inteface %q, want %q", ni, "user")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeAndTime(t *testing.T) {
|
||||
// Time is February 5, 1998 12:30:23.136364800 AM GMT
|
||||
|
||||
uuid := Parse("7d444840-9dc0-11d1-b245-5ffdce74fad2")
|
||||
node := []byte{0x5f, 0xfd, 0xce, 0x74, 0xfa, 0xd2}
|
||||
|
||||
ts, ok := uuid.Time()
|
||||
if ok {
|
||||
c := time.Unix(ts.UnixTime())
|
||||
want := time.Date(1998, 2, 5, 0, 30, 23, 136364800, time.UTC)
|
||||
if !c.Equal(want) {
|
||||
t.Errorf("Got time %v, want %v", c, want)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("%s: bad time", uuid)
|
||||
}
|
||||
if !bytes.Equal(node, uuid.NodeID()) {
|
||||
t.Errorf("Expected node %v got %v", node, uuid.NodeID())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMD5(t *testing.T) {
|
||||
uuid := NewMD5(NameSpace_DNS, []byte("python.org")).String()
|
||||
want := "6fa459ea-ee8a-3ca4-894e-db77e160355e"
|
||||
if uuid != want {
|
||||
t.Errorf("MD5: got %q expected %q", uuid, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSHA1(t *testing.T) {
|
||||
uuid := NewSHA1(NameSpace_DNS, []byte("python.org")).String()
|
||||
want := "886313e1-3b8a-5372-9b90-0c9aee199e5d"
|
||||
if uuid != want {
|
||||
t.Errorf("SHA1: got %q expected %q", uuid, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeID(t *testing.T) {
|
||||
nid := []byte{1, 2, 3, 4, 5, 6}
|
||||
nodeID = nil // Reset global state for next test
|
||||
SetNodeInterface("")
|
||||
s := NodeInterface()
|
||||
if runtime.GOARCH != "js" {
|
||||
if s == "" || s == "user" {
|
||||
t.Errorf("NodeInterface %q after SetInterface", s)
|
||||
}
|
||||
}
|
||||
node1 := NodeID()
|
||||
if node1 == nil {
|
||||
t.Error("NodeID nil after SetNodeInterface", s)
|
||||
}
|
||||
SetNodeID(nid)
|
||||
s = NodeInterface()
|
||||
if s != "user" {
|
||||
t.Errorf("Expected NodeInterface %q got %q", "user", s)
|
||||
}
|
||||
node2 := NodeID()
|
||||
if node2 == nil {
|
||||
t.Error("NodeID nil after SetNodeID", s)
|
||||
}
|
||||
if bytes.Equal(node1, node2) {
|
||||
t.Error("NodeID not changed after SetNodeID", s)
|
||||
} else if !bytes.Equal(nid, node2) {
|
||||
t.Errorf("NodeID is %x, expected %x", node2, nid)
|
||||
}
|
||||
}
|
||||
|
||||
func testDCE(t *testing.T, name string, uuid UUID, domain Domain, id uint32) {
|
||||
if uuid == nil {
|
||||
t.Errorf("%s failed", name)
|
||||
return
|
||||
}
|
||||
if v, _ := uuid.Version(); v != 2 {
|
||||
t.Errorf("%s: %s: expected version 2, got %s", name, uuid, v)
|
||||
return
|
||||
}
|
||||
if v, ok := uuid.Domain(); !ok || v != domain {
|
||||
if !ok {
|
||||
t.Errorf("%s: %d: Domain failed", name, uuid)
|
||||
} else {
|
||||
t.Errorf("%s: %s: expected domain %d, got %d", name, uuid, domain, v)
|
||||
}
|
||||
}
|
||||
if v, ok := uuid.Id(); !ok || v != id {
|
||||
if !ok {
|
||||
t.Errorf("%s: %d: Id failed", name, uuid)
|
||||
} else {
|
||||
t.Errorf("%s: %s: expected id %d, got %d", name, uuid, id, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDCE(t *testing.T) {
|
||||
testDCE(t, "NewDCESecurity", NewDCESecurity(42, 12345678), 42, 12345678)
|
||||
testDCE(t, "NewDCEPerson", NewDCEPerson(), Person, uint32(os.Getuid()))
|
||||
testDCE(t, "NewDCEGroup", NewDCEGroup(), Group, uint32(os.Getgid()))
|
||||
}
|
||||
|
||||
type badRand struct{}
|
||||
|
||||
func (r badRand) Read(buf []byte) (int, error) {
|
||||
for i, _ := range buf {
|
||||
buf[i] = byte(i)
|
||||
}
|
||||
return len(buf), nil
|
||||
}
|
||||
|
||||
func TestBadRand(t *testing.T) {
|
||||
SetRand(badRand{})
|
||||
uuid1 := New()
|
||||
uuid2 := New()
|
||||
if uuid1 != uuid2 {
|
||||
t.Errorf("expected duplicates, got %q and %q", uuid1, uuid2)
|
||||
}
|
||||
SetRand(nil)
|
||||
uuid1 = New()
|
||||
uuid2 = New()
|
||||
if uuid1 == uuid2 {
|
||||
t.Errorf("unexpected duplicates, got %q", uuid1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUUID_Array(t *testing.T) {
|
||||
expect := Array{
|
||||
0xf4, 0x7a, 0xc1, 0x0b,
|
||||
0x58, 0xcc,
|
||||
0x03, 0x72,
|
||||
0x85, 0x67,
|
||||
0x0e, 0x02, 0xb2, 0xc3, 0xd4, 0x79,
|
||||
}
|
||||
uuid := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479")
|
||||
if uuid == nil {
|
||||
t.Fatal("invalid uuid")
|
||||
}
|
||||
if uuid.Array() != expect {
|
||||
t.Fatal("invalid array")
|
||||
}
|
||||
}
|
||||
|
||||
func TestArray_UUID(t *testing.T) {
|
||||
array := Array{
|
||||
0xf4, 0x7a, 0xc1, 0x0b,
|
||||
0x58, 0xcc,
|
||||
0x03, 0x72,
|
||||
0x85, 0x67,
|
||||
0x0e, 0x02, 0xb2, 0xc3, 0xd4, 0x79,
|
||||
}
|
||||
expect := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479")
|
||||
if expect == nil {
|
||||
t.Fatal("invalid uuid")
|
||||
}
|
||||
if !bytes.Equal(array.UUID(), expect) {
|
||||
t.Fatal("invalid uuid")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParse(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
uuid := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479")
|
||||
if uuid == nil {
|
||||
b.Fatal("invalid uuid")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNew(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
New()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUUID_String(b *testing.B) {
|
||||
uuid := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479")
|
||||
if uuid == nil {
|
||||
b.Fatal("invalid uuid")
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
if uuid.String() == "" {
|
||||
b.Fatal("invalid uuid")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUUID_URN(b *testing.B) {
|
||||
uuid := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479")
|
||||
if uuid == nil {
|
||||
b.Fatal("invalid uuid")
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
if uuid.URN() == "" {
|
||||
b.Fatal("invalid uuid")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUUID_Array(b *testing.B) {
|
||||
expect := Array{
|
||||
0xf4, 0x7a, 0xc1, 0x0b,
|
||||
0x58, 0xcc,
|
||||
0x03, 0x72,
|
||||
0x85, 0x67,
|
||||
0x0e, 0x02, 0xb2, 0xc3, 0xd4, 0x79,
|
||||
}
|
||||
uuid := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479")
|
||||
if uuid == nil {
|
||||
b.Fatal("invalid uuid")
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
if uuid.Array() != expect {
|
||||
b.Fatal("invalid array")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkArray_UUID(b *testing.B) {
|
||||
array := Array{
|
||||
0xf4, 0x7a, 0xc1, 0x0b,
|
||||
0x58, 0xcc,
|
||||
0x03, 0x72,
|
||||
0x85, 0x67,
|
||||
0x0e, 0x02, 0xb2, 0xc3, 0xd4, 0x79,
|
||||
}
|
||||
expect := Parse("f47ac10b-58cc-0372-8567-0e02b2c3d479")
|
||||
if expect == nil {
|
||||
b.Fatal("invalid uuid")
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
if !bytes.Equal(array.UUID(), expect) {
|
||||
b.Fatal("invalid uuid")
|
||||
}
|
||||
}
|
||||
}
|
||||
39
vendor/github.com/pborman/uuid/version1.go
generated
vendored
Normal file
39
vendor/github.com/pborman/uuid/version1.go
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
// NewUUID returns a Version 1 UUID based on the current NodeID and clock
|
||||
// sequence, and the current time. If the NodeID has not been set by SetNodeID
|
||||
// or SetNodeInterface then it will be set automatically. If the NodeID cannot
|
||||
// be set NewUUID returns nil. If clock sequence has not been set by
|
||||
// SetClockSequence then it will be set automatically. If GetTime fails to
|
||||
// return the current NewUUID returns nil.
|
||||
func NewUUID() UUID {
|
||||
SetNodeInterface("")
|
||||
|
||||
now, seq, err := GetTime()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
uuid := make([]byte, 16)
|
||||
|
||||
time_low := uint32(now & 0xffffffff)
|
||||
time_mid := uint16((now >> 32) & 0xffff)
|
||||
time_hi := uint16((now >> 48) & 0x0fff)
|
||||
time_hi |= 0x1000 // Version 1
|
||||
|
||||
binary.BigEndian.PutUint32(uuid[0:], time_low)
|
||||
binary.BigEndian.PutUint16(uuid[4:], time_mid)
|
||||
binary.BigEndian.PutUint16(uuid[6:], time_hi)
|
||||
binary.BigEndian.PutUint16(uuid[8:], seq)
|
||||
copy(uuid[10:], nodeID)
|
||||
|
||||
return uuid
|
||||
}
|
||||
25
vendor/github.com/pborman/uuid/version4.go
generated
vendored
Normal file
25
vendor/github.com/pborman/uuid/version4.go
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2011 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package uuid
|
||||
|
||||
// Random returns a Random (Version 4) UUID or panics.
|
||||
//
|
||||
// The strength of the UUIDs is based on the strength of the crypto/rand
|
||||
// package.
|
||||
//
|
||||
// A note about uniqueness derived from from the UUID Wikipedia entry:
|
||||
//
|
||||
// Randomly generated UUIDs have 122 random bits. One's annual risk of being
|
||||
// hit by a meteorite is estimated to be one chance in 17 billion, that
|
||||
// means the probability is about 0.00000000006 (6 × 10−11),
|
||||
// equivalent to the odds of creating a few tens of trillions of UUIDs in a
|
||||
// year and having one duplicate.
|
||||
func NewRandom() UUID {
|
||||
uuid := make([]byte, 16)
|
||||
randomBits([]byte(uuid))
|
||||
uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
|
||||
uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
|
||||
return uuid
|
||||
}
|
||||
30
vendor/k8s.io/apimachinery/pkg/util/uuid/BUILD
generated
vendored
Normal file
30
vendor/k8s.io/apimachinery/pkg/util/uuid/BUILD
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["uuid.go"],
|
||||
importmap = "k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/util/uuid",
|
||||
importpath = "k8s.io/apimachinery/pkg/util/uuid",
|
||||
deps = [
|
||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//vendor/github.com/pborman/uuid:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
43
vendor/k8s.io/apimachinery/pkg/util/uuid/uuid.go
generated
vendored
Normal file
43
vendor/k8s.io/apimachinery/pkg/util/uuid/uuid.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
Copyright 2014 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 uuid
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/pborman/uuid"
|
||||
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
var uuidLock sync.Mutex
|
||||
var lastUUID uuid.UUID
|
||||
|
||||
func NewUUID() types.UID {
|
||||
uuidLock.Lock()
|
||||
defer uuidLock.Unlock()
|
||||
result := uuid.NewUUID()
|
||||
// The UUID package is naive and can generate identical UUIDs if the
|
||||
// time interval is quick enough.
|
||||
// The UUID uses 100 ns increments so it's short enough to actively
|
||||
// wait for a new value.
|
||||
for uuid.Equal(lastUUID, result) == true {
|
||||
result = uuid.NewUUID()
|
||||
}
|
||||
lastUUID = result
|
||||
return types.UID(result.String())
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/virtual-kubelet/virtual-kubelet/providers/azure"
|
||||
"github.com/virtual-kubelet/virtual-kubelet/providers/azurebatch"
|
||||
"github.com/virtual-kubelet/virtual-kubelet/providers/cri"
|
||||
"github.com/virtual-kubelet/virtual-kubelet/providers/huawei"
|
||||
"github.com/virtual-kubelet/virtual-kubelet/providers/hypersh"
|
||||
"github.com/virtual-kubelet/virtual-kubelet/providers/mock"
|
||||
"github.com/virtual-kubelet/virtual-kubelet/providers/vic"
|
||||
@@ -121,6 +122,11 @@ func New(nodeName, operatingSystem, namespace, kubeConfig, taint, provider, prov
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "huawei":
|
||||
p, err = huawei.NewCCIProvider(providerConfig, rm, nodeName, operatingSystem, internalIP, daemonEndpointPort)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
fmt.Printf("Provider '%s' is not supported\n", provider)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user