github.com/theblckswan/cni@v0.8.1/pkg/version/plugin.go (about) 1 // Copyright 2016 CNI authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package version 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "io" 21 "strconv" 22 "strings" 23 ) 24 25 // PluginInfo reports information about CNI versioning 26 type PluginInfo interface { 27 // SupportedVersions returns one or more CNI spec versions that the plugin 28 // supports. If input is provided in one of these versions, then the plugin 29 // promises to use the same CNI version in its response 30 SupportedVersions() []string 31 32 // Encode writes this CNI version information as JSON to the given Writer 33 Encode(io.Writer) error 34 } 35 36 type pluginInfo struct { 37 CNIVersion_ string `json:"cniVersion"` 38 SupportedVersions_ []string `json:"supportedVersions,omitempty"` 39 } 40 41 // pluginInfo implements the PluginInfo interface 42 var _ PluginInfo = &pluginInfo{} 43 44 func (p *pluginInfo) Encode(w io.Writer) error { 45 return json.NewEncoder(w).Encode(p) 46 } 47 48 func (p *pluginInfo) SupportedVersions() []string { 49 return p.SupportedVersions_ 50 } 51 52 // PluginSupports returns a new PluginInfo that will report the given versions 53 // as supported 54 func PluginSupports(supportedVersions ...string) PluginInfo { 55 if len(supportedVersions) < 1 { 56 panic("programmer error: you must support at least one version") 57 } 58 return &pluginInfo{ 59 CNIVersion_: Current(), 60 SupportedVersions_: supportedVersions, 61 } 62 } 63 64 // PluginDecoder can decode the response returned by a plugin's VERSION command 65 type PluginDecoder struct{} 66 67 func (*PluginDecoder) Decode(jsonBytes []byte) (PluginInfo, error) { 68 var info pluginInfo 69 err := json.Unmarshal(jsonBytes, &info) 70 if err != nil { 71 return nil, fmt.Errorf("decoding version info: %s", err) 72 } 73 if info.CNIVersion_ == "" { 74 return nil, fmt.Errorf("decoding version info: missing field cniVersion") 75 } 76 if len(info.SupportedVersions_) == 0 { 77 if info.CNIVersion_ == "0.2.0" { 78 return PluginSupports("0.1.0", "0.2.0"), nil 79 } 80 return nil, fmt.Errorf("decoding version info: missing field supportedVersions") 81 } 82 return &info, nil 83 } 84 85 // ParseVersion parses a version string like "3.0.1" or "0.4.5" into major, 86 // minor, and micro numbers or returns an error 87 func ParseVersion(version string) (int, int, int, error) { 88 var major, minor, micro int 89 if version == "" { 90 return -1, -1, -1, fmt.Errorf("invalid version %q: the version is empty", version) 91 } 92 93 parts := strings.Split(version, ".") 94 if len(parts) >= 4 { 95 return -1, -1, -1, fmt.Errorf("invalid version %q: too many parts", version) 96 } 97 98 major, err := strconv.Atoi(parts[0]) 99 if err != nil { 100 return -1, -1, -1, fmt.Errorf("failed to convert major version part %q: %v", parts[0], err) 101 } 102 103 if len(parts) >= 2 { 104 minor, err = strconv.Atoi(parts[1]) 105 if err != nil { 106 return -1, -1, -1, fmt.Errorf("failed to convert minor version part %q: %v", parts[1], err) 107 } 108 } 109 110 if len(parts) >= 3 { 111 micro, err = strconv.Atoi(parts[2]) 112 if err != nil { 113 return -1, -1, -1, fmt.Errorf("failed to convert micro version part %q: %v", parts[2], err) 114 } 115 } 116 117 return major, minor, micro, nil 118 } 119 120 // GreaterThanOrEqualTo takes two string versions, parses them into major/minor/micro 121 // numbers, and compares them to determine whether the first version is greater 122 // than or equal to the second 123 func GreaterThanOrEqualTo(version, otherVersion string) (bool, error) { 124 firstMajor, firstMinor, firstMicro, err := ParseVersion(version) 125 if err != nil { 126 return false, err 127 } 128 129 secondMajor, secondMinor, secondMicro, err := ParseVersion(otherVersion) 130 if err != nil { 131 return false, err 132 } 133 134 if firstMajor > secondMajor { 135 return true, nil 136 } else if firstMajor == secondMajor { 137 if firstMinor > secondMinor { 138 return true, nil 139 } else if firstMinor == secondMinor && firstMicro >= secondMicro { 140 return true, nil 141 } 142 } 143 return false, nil 144 }