Skip to content

Commit

Permalink
go, api: Generate golang api from the rust api AST
Browse files Browse the repository at this point in the history
At some scenarios is useful to have a "structured" version of the
network_state, this change use the syn rust crate to inspect rust api
AST and generate a golang API, it also add calls at nmstate golang
binding to use it.

Signed-off-by: Enrique Llorente <ellorent@redhat.com>
  • Loading branch information
qinqon committed May 11, 2023
1 parent 8ea675b commit 070254a
Show file tree
Hide file tree
Showing 21 changed files with 4,484 additions and 5 deletions.
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,12 @@ clib_check: $(CLIB_SO_DEV_DEBUG) $(CLIB_HEADER)
$(TMPDIR)/nmpolicy_yaml_test 1>/dev/null
rm -rf $(TMPDIR)

.PHONY: go_generate
go_generate:
cd rust/src/go/nmstate; go generate

.PHONY: go_check
go_check: $(CLIB_SO_DEV_DEBUG) $(CLIB_HEADER)
go_check: $(CLIB_SO_DEV_DEBUG) $(CLIB_HEADER) go_generate
$(eval TMPDIR := $(shell mktemp -d))
cp $(CLIB_SO_DEV_DEBUG) $(TMPDIR)/$(CLIB_SO_FULL)
ln -sfv $(CLIB_SO_FULL) $(TMPDIR)/$(CLIB_SO_MAN)
Expand Down
1 change: 0 additions & 1 deletion examples/dns_remove.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
---
dns-resolver:
config: {}
interfaces: []
1 change: 1 addition & 0 deletions rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ members = [
"src/cli",
"src/clib",
"src/lib",
"src/go/apigen",
]
45 changes: 45 additions & 0 deletions rust/src/go/api/api_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package nmstate

import (
"fmt"
"io/fs"
"io/ioutil"
"path/filepath"
"testing"

"sigs.k8s.io/yaml"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestUnmarshallingExamples(t *testing.T) {
examples := map[string][]byte{}
err := filepath.Walk("../../../../examples", func(path string, info fs.FileInfo, err error) error {
if info.IsDir() && info.Name() == "policy" {
return filepath.SkipDir
}
if info.IsDir() || filepath.Ext(info.Name()) != ".yml" {
return nil
}
example, err := ioutil.ReadFile(path)
if err != nil {
return fmt.Errorf("failed reading example '%s': %v", info.Name(), err)
}
examples[info.Name()] = example
return nil
})
require.NoError(t, err, "must succeed reading examples")
for exampleName, expectedExample := range examples {
t.Run(exampleName, func(t *testing.T) {
netState := &NetworkState{}
err := yaml.Unmarshal(expectedExample, netState)
assert.NoError(t, err, "must success unmarshaling example")

obtainedExample, err := yaml.Marshal(netState)
assert.NoError(t, err, "must success marshaling example")

assert.YAMLEq(t, string(expectedExample), string(obtainedExample), "expected: \n%s\nobtained: \n%s\n", expectedExample, obtainedExample)
})
}
}
16 changes: 16 additions & 0 deletions rust/src/go/api/boilerplate.go.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
Copyright The NMState 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.
*/

18 changes: 18 additions & 0 deletions rust/src/go/api/bridge_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package nmstate

// +k8s:deepcopy-gen=true
type LinuxBridgeInterface struct {
// +kubebuilder:validation:Type=object
// +kubebuilder:validation:Schemaless
// +kubebuilder:pruning:PreserveUnknownFields
Bridge *LinuxBridgeConfig `json:"bridge,omitempty",json:"ovs-bridge,omitempty"`
OvsBridge *LinuxBridgeConfig `json:"linux-bridge,omitempty",json:"ovs-bridge,omitempty"`
}

// +k8s:deepcopy-gen=true
type OvsBridgeInterface struct {
// +kubebuilder:validation:Type=object
// +kubebuilder:validation:Schemaless
Bridge *OvsBridgeConfig `json:"bridge,omitempty",json:"ovs-bridge,omitempty"`
OvsBridge *OvsBridgeConfig `json:"ovs-bridge,omitempty",json:"ovs-bridge,omitempty"`
}
3 changes: 3 additions & 0 deletions rust/src/go/api/controller-gen.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash -e

GOFLAGS=-mod=mod go run sigs.k8s.io/controller-tools/cmd/controller-gen@v0.8.0 $@
211 changes: 211 additions & 0 deletions rust/src/go/api/encoding.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
package nmstate

import (
"encoding/json"
"fmt"
)

func (i Interface) MarshalJSON() ([]byte, error) {
if i.LinuxBridge != nil {
i.BaseInterface.IfaceType = InterfaceTypeLinuxBridge
return json.Marshal(&struct {
*BaseInterface
*LinuxBridgeInterface
}{
&i.BaseInterface,
i.LinuxBridge,
})
} else if i.Ethernet != nil {
if i.BaseInterface.IfaceType == "" ||
i.BaseInterface.IfaceType == InterfaceTypeUnknown {
if i.Ethernet.Veth != nil {
i.BaseInterface.IfaceType = InterfaceTypeVeth
} else {
i.BaseInterface.IfaceType = InterfaceTypeEthernet
}
}
return json.Marshal(&struct {
*BaseInterface
*EthernetInterface
}{
&i.BaseInterface,
i.Ethernet,
})
} else if i.Bond != nil {
i.BaseInterface.IfaceType = InterfaceTypeBond
return json.Marshal(&struct {
*BaseInterface
*BondInterface
}{
&i.BaseInterface,
i.Bond,
})
} else if i.Dummy != nil {
i.BaseInterface.IfaceType = InterfaceTypeDummy
return json.Marshal(&struct {
*BaseInterface
*DummyInterface
}{
&i.BaseInterface,
i.Dummy,
})
} else if i.Vlan != nil {
i.BaseInterface.IfaceType = InterfaceTypeVlan
return json.Marshal(&struct {
*BaseInterface
*VlanInterface
}{
&i.BaseInterface,
i.Vlan,
})
} else if i.OvsBridge != nil {
i.BaseInterface.IfaceType = InterfaceTypeOvsBridge
return json.Marshal(&struct {
*BaseInterface
*OvsBridgeInterface
}{
&i.BaseInterface,
i.OvsBridge,
})
} else if i.OvsInterface != nil {
i.BaseInterface.IfaceType = InterfaceTypeOvsInterface
return json.Marshal(&struct {
*BaseInterface
*OvsInterface
}{
&i.BaseInterface,
i.OvsInterface,
})
} else if i.MacVlan != nil {
i.BaseInterface.IfaceType = InterfaceTypeMacVlan
return json.Marshal(&struct {
*BaseInterface
*MacVlanInterface
}{
&i.BaseInterface,
i.MacVlan,
})
} else if i.MacVtap != nil {
i.BaseInterface.IfaceType = InterfaceTypeMacVtap
return json.Marshal(&struct {
*BaseInterface
*MacVtapInterface
}{
&i.BaseInterface,
i.MacVtap,
})
} else if i.Vrf != nil {
i.BaseInterface.IfaceType = InterfaceTypeVrf
return json.Marshal(&struct {
*BaseInterface
*VrfInterface
}{
&i.BaseInterface,
i.Vrf,
})
} else if i.InfiniBand != nil {
i.BaseInterface.IfaceType = InterfaceTypeInfiniBand
return json.Marshal(&struct {
*BaseInterface
*InfiniBandInterface
}{
&i.BaseInterface,
i.InfiniBand,
})
} else if i.Unknown != nil {
return json.Marshal(&struct {
*BaseInterface
*UnknownInterface
}{
&i.BaseInterface,
i.Unknown,
})
}
return nil, fmt.Errorf("unknown interface")
}

func (i *Interface) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, &i.BaseInterface); err != nil {
return err
}
if i.BaseInterface.IfaceType == InterfaceTypeLinuxBridge {
i.LinuxBridge = &LinuxBridgeInterface{}
if err := json.Unmarshal(data, i.LinuxBridge); err != nil {
return err
}
return nil
} else if i.BaseInterface.IfaceType == InterfaceTypeEthernet {
i.Ethernet = &EthernetInterface{}
if err := json.Unmarshal(data, i.Ethernet); err != nil {
return err
}
return nil
} else if i.BaseInterface.IfaceType == InterfaceTypeBond {
i.Bond = &BondInterface{}
if err := json.Unmarshal(data, i.Bond); err != nil {
return err
}
return nil
} else if i.BaseInterface.IfaceType == InterfaceTypeVlan {
i.Vlan = &VlanInterface{}
if err := json.Unmarshal(data, i.Vlan); err != nil {
return err
}
return nil
} else if i.BaseInterface.IfaceType == InterfaceTypeOvsBridge {
i.OvsBridge = &OvsBridgeInterface{}
if err := json.Unmarshal(data, i.OvsBridge); err != nil {
return err
}
return nil
} else if i.BaseInterface.IfaceType == InterfaceTypeOvsInterface {
i.OvsInterface = &OvsInterface{}
if err := json.Unmarshal(data, i.OvsInterface); err != nil {
return err
}
return nil
} else if i.BaseInterface.IfaceType == InterfaceTypeMacVlan {
i.MacVlan = &MacVlanInterface{}
if err := json.Unmarshal(data, i.MacVlan); err != nil {
return err
}
return nil
} else if i.BaseInterface.IfaceType == InterfaceTypeMacVtap {
i.MacVtap = &MacVtapInterface{}
if err := json.Unmarshal(data, i.MacVtap); err != nil {
return err
}
return nil
} else if i.BaseInterface.IfaceType == InterfaceTypeVrf {
i.Vrf = &VrfInterface{}
if err := json.Unmarshal(data, i.Vrf); err != nil {
return err
}
return nil
} else if i.BaseInterface.IfaceType == InterfaceTypeVeth {
i.Ethernet = &EthernetInterface{}
if err := json.Unmarshal(data, i.Ethernet); err != nil {
return err
}
return nil
} else if i.BaseInterface.IfaceType == InterfaceTypeInfiniBand {
i.InfiniBand = &InfiniBandInterface{}
if err := json.Unmarshal(data, i.InfiniBand); err != nil {
return err
}
return nil
} else if i.BaseInterface.IfaceType == InterfaceTypeDummy {
i.Dummy = &DummyInterface{}
if err := json.Unmarshal(data, i.Dummy); err != nil {
return err
}
return nil

} else {
i.Unknown = &UnknownInterface{}
if err := json.Unmarshal(data, i.Unknown); err != nil {
return err
}
return nil
}
}
8 changes: 8 additions & 0 deletions rust/src/go/api/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module github.com/qinqon/nmstate/rust/src/go/api/v2

go 1.16

require (
github.com/stretchr/testify v1.7.0
sigs.k8s.io/yaml v1.3.0
)
16 changes: 16 additions & 0 deletions rust/src/go/api/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
5 changes: 5 additions & 0 deletions rust/src/go/api/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//go:generate cargo run --manifest-path ../apigen/Cargo.toml -- zz_generated.types.go
//go:generate ./controller-gen.sh object:headerFile="boilerplate.go.txt" paths="."
//go:generate go fmt types.go

package nmstate

0 comments on commit 070254a

Please sign in to comment.