@@ -16,6 +16,7 @@ package invoke
16
16
17
17
import (
18
18
"context"
19
+ "encoding/json"
19
20
"fmt"
20
21
"os"
21
22
@@ -33,6 +34,43 @@ type Exec interface {
33
34
Decode (jsonBytes []byte ) (version.PluginInfo , error )
34
35
}
35
36
37
+ // Plugin must return result in same version as specified in netconf; but
38
+ // for backwards compatibility reasons if the result version is empty use
39
+ // config version (rather than technically correct 0.1.0).
40
+ // https://github.com/containernetworking/cni/issues/895
41
+ func fixupResultVersion (netconf , result []byte ) (string , []byte , error ) {
42
+ versionDecoder := & version.ConfigDecoder {}
43
+ confVersion , err := versionDecoder .Decode (netconf )
44
+ if err != nil {
45
+ return "" , nil , err
46
+ }
47
+
48
+ var rawResult map [string ]interface {}
49
+ if err := json .Unmarshal (result , & rawResult ); err != nil {
50
+ return "" , nil , fmt .Errorf ("failed to unmarshal raw result: %w" , err )
51
+ }
52
+
53
+ // Manually decode Result version; we need to know whether its cniVersion
54
+ // is empty, while built-in decoders (correctly) substitute 0.1.0 for an
55
+ // empty version per the CNI spec.
56
+ if resultVerRaw , ok := rawResult ["cniVersion" ]; ok {
57
+ resultVer , ok := resultVerRaw .(string )
58
+ if ok && resultVer != "" {
59
+ return resultVer , result , nil
60
+ }
61
+ }
62
+
63
+ // If the cniVersion is not present or empty, assume the result is
64
+ // the same CNI spec version as the config
65
+ rawResult ["cniVersion" ] = confVersion
66
+ newBytes , err := json .Marshal (rawResult )
67
+ if err != nil {
68
+ return "" , nil , fmt .Errorf ("failed to remarshal fixed result: %w" , err )
69
+ }
70
+
71
+ return confVersion , newBytes , nil
72
+ }
73
+
36
74
// For example, a testcase could pass an instance of the following fakeExec
37
75
// object to ExecPluginWithResult() to verify the incoming stdin and environment
38
76
// and provide a tailored response:
@@ -84,7 +122,12 @@ func ExecPluginWithResult(ctx context.Context, pluginPath string, netconf []byte
84
122
return nil , err
85
123
}
86
124
87
- return create .CreateFromBytes (stdoutBytes )
125
+ resultVersion , fixedBytes , err := fixupResultVersion (netconf , stdoutBytes )
126
+ if err != nil {
127
+ return nil , err
128
+ }
129
+
130
+ return create .Create (resultVersion , fixedBytes )
88
131
}
89
132
90
133
func ExecPluginWithoutResult (ctx context.Context , pluginPath string , netconf []byte , args CNIArgs , exec Exec ) error {
0 commit comments