How to create go-lang library for NSX-V
First some questions:
- Why would you like to create a library for NSX-V with Go-Lang
- Why would you use golang
The go languange is a very powerful language and ideal for microservices as its lightweight
The answers:
- Because you still need to be able to work with a NSX-V while migrating towards NSX-T
- Because you create a application written in golang which need to be able to connect to nsx-v
- The migration to NSX-T is written in goLang and you need a nsx-v library for that
- It’s cool to write it in GO.
NSX-V does not have a swagger endpoint. Without a swagger interface it’s hard to create a library for any programming language.
A few years ago Andrew Voltmer created a postman collection nsxvapi.postman_collection.json. This is perfect when you just want use postman for the REST calls.
But there is a way to make a Go library form it. And yes i know. why should you create a library for NSX-V while we have NSX-T.
First of all we need Postman. With postman we can import the downloaded collection described above or open on the website the RAW function and copy all. Open postman and click import. A dialog box opens then select Raw and paste the file in the area where you should paste it in.
An error occurs when you are using the newer version of postman.
But Postman gave a solution for that. Converting-postman-collections-from-v1-to-v2
If you do not have nodejs and npm, you first need to install it on your laptop instruction here .
sudo npm install -g postman-collection-transformer
Lets assume you saved the file form above as nsxvapi.postman_collection.json
postman-collection-transformer convert -i ./nsxvapi.postman_collection.json -o ./nsxvapi.postman_collection_v2.json -j 1.0.0 -p 2.0.0 -P
Now the file is converted to version 2 and we can import the file in postman. as shown below.
But is it really usable for the generating swagger for this collection, No not really.
To avoid the hassle for you, i organised the collection in a more structured way, you can download it here.
With this file we can create a swagger file which will result into a go library 😉 .
To generate a swagger file from the postman collection Jorge created a postman to openapi tool, which can be found here.
Install and execute the following commands.
npm i postman-to-openapi -g
p2o <YOUR FILE PATH: ./prepare/nsxv-postman-collection-v2.1.json> -f <YOUR NEW FILE PATH: ./prepare/nsxv-open-api-3.0.yml>
So now we have finally a swagger file which we can use to generate code. But first you need to install something again on your machine.
The swagger code generator which can generate code in different languages.
When you want to have a package name which is not the default swagger. A config file need to be included in the cli command, and of course you need to create one.
{
"packageName": "nsxv"
}
Execute the following line to generate for the swagger file.
swagger-codegen generate -i ./prepare/nsxv-open-api-3.0.yml -l go -o ./nsxv -c ./prepare/nsxv-config.json
The library is created but some weird names are given for filenames, functions, classes and localVar. What i did is renamed in Visual Studio Code for example api10 with nothing and api20 with nothing. Renamed the filenames from api_ai.go to ai.go. Command line mmv 'api_' '#1#2'
So we have a library what we can use, But we are still missing something.
Yes thats right the interfaces. I added an interface generator. Which was created with println by survivorbat who is a colleague of the client where i work. i modified it to generate an interface file. Can be found here
package main
import (
"fmt"
"os"
"reflect"
"strings"
"github.com/teetgerink/nsxv-golang-library.git/nsxv"
)
//PACKAGENAME name for the package where the generated interfaces are located
const PACKAGENAME = "nsxv"
// REFERENCENAME is the name that is generated from the API Client
const REFERENCENAME = "*" + PACKAGENAME + "."
//WORKINGDIR the working directory is based on the package name
const WORKINGDIR = "./" + PACKAGENAME
// This script generates the contents of the `interfaces.go`
func main() {
workingDir, _ := os.Getwd()
fmt.Printf("Current working directory %v \n", workingDir)
os.Chdir(WORKINGDIR)
workingDir, _ = os.Getwd()
fmt.Printf("New working directory %v \n", workingDir)
item := reflect.TypeOf(nsxv.APIClient{})
iFile, err := os.Create("interfaces.go")
if err != nil {
panic(err)
}
defer iFile.Close()
// Generate interface methods
// generate Package name
generateLines(iFile, fmt.Sprintf("package %v\n\n ", PACKAGENAME))
// generate imports
generateLines(iFile, fmt.Sprintf("import (\n \"context\"\n \"net/http\"\n)\n\n"))
generateLines(iFile, fmt.Sprintf("// Compile-time interface checks\n"))
generateLines(iFile, fmt.Sprintf("var _ IApiClient = &ApiClient{}\n\n"))
generateLines(iFile, fmt.Sprintf("// IApiClient is the main interface for the library\n"))
generateLines(iFile, fmt.Sprintf("type IApiClient interface {\n"))
generateLines(iFile, fmt.Sprintf(" GetContext() context.Context\n"))
for index := 0; index < item.NumField(); index++ {
prop := item.Field(index)
generateLines(iFile, fmt.Sprintf(" Get%v() I%v\n", prop.Name, prop.Name))
}
generateLines(iFile, fmt.Sprintf("}\n\n"))
generateLines(iFile, fmt.Sprintf("type ApiClient struct {\n"))
generateLines(iFile, fmt.Sprintf(" APIClient \n Context context.Context\n}\n\n"))
generateLines(iFile, fmt.Sprintf("func (a *ApiClient) GetContext() context.Context {\n"))
generateLines(iFile, fmt.Sprintf(" return a.Context\n}\n\n"))
// Generate return methods of the api client
for index := 0; index < item.NumField(); index++ {
prop := item.Field(index)
funcName := fmt.Sprintf("func (a *ApiClient) Get%v() I%v {\n"+
" return a.%v\n"+
"}\n\n", prop.Name, prop.Name, prop.Name)
generateLines(iFile, funcName)
}
// Generate the interfaces
for index := 0; index < item.NumField(); index++ {
prop := item.Field(index)
generateLines(iFile, fmt.Sprintf("type I%v interface {\n", prop.Name))
for funcIndex := 0; funcIndex < prop.Type.NumMethod(); funcIndex++ {
method := prop.Type.Method(funcIndex)
generateLines(iFile, fmt.Sprintf(" %v(", method.Name))
if method.Type.Kind() != reflect.Func {
continue
}
var inArgs []string
for argIndex := 1; argIndex < method.Type.NumIn(); argIndex++ {
arg := method.Type.In(argIndex)
strArg := strings.Replace(arg.String(), REFERENCENAME, "*", -1)
inArgs = append(inArgs, strArg)
}
generateLines(iFile, fmt.Sprintf("%v)", strings.Join(inArgs, ", ")))
var outArgs []string
for returnIndex := 0; returnIndex < method.Type.NumOut(); returnIndex++ {
returnValue := method.Type.Out(returnIndex)
outArgs = append(outArgs, returnValue.String())
}
generateLines(iFile, fmt.Sprintf("(%v)\n", strings.Join(outArgs, ", ")))
}
generateLines(iFile, fmt.Sprintf("}\n\n"))
}
}
func generateLines(iFile *os.File, value string) {
_, err := fmt.Fprint(iFile, value)
if err != nil {
fmt.Printf("error occured %v", err)
panic(err)
}
fmt.Print(value)
}
The complete library and the files to create the nsxv library are downloadable from https://github.com/teetgerink/nsxv-golang-library