@@ -16,6 +16,7 @@ package invoke
1616
1717import (
1818 "context"
19+ "encoding/json"
1920 "fmt"
2021 "os"
2122
@@ -33,6 +34,43 @@ type Exec interface {
3334 Decode (jsonBytes []byte ) (version.PluginInfo , error )
3435}
3536
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+
3674// For example, a testcase could pass an instance of the following fakeExec
3775// object to ExecPluginWithResult() to verify the incoming stdin and environment
3876// and provide a tailored response:
@@ -84,7 +122,12 @@ func ExecPluginWithResult(ctx context.Context, pluginPath string, netconf []byte
84122 return nil , err
85123 }
86124
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 )
88131}
89132
90133func ExecPluginWithoutResult (ctx context.Context , pluginPath string , netconf []byte , args CNIArgs , exec Exec ) error {
0 commit comments