Skip to content
Snippets Groups Projects
write.go 6.18 KiB
Newer Older
  • Learn to ignore specific revisions
  • package csbi
    
    import (
    	"bytes"
    	"context"
    	"fmt"
    	"io/fs"
    	"net"
    	"os"
    	"os/exec"
    	"path/filepath"
    	"strings"
    
    	spb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/southbound"
    	"github.com/openconfig/ygot/genutil"
    
    	"github.com/openconfig/ygot/gogen"
    
    	log "github.com/sirupsen/logrus"
    	"github.com/spf13/viper"
    	codes "google.golang.org/grpc/codes"
    	"google.golang.org/grpc/peer"
    	status "google.golang.org/grpc/status"
    	"gopkg.in/yaml.v3"
    )
    
    // write takes a ygen.Generatedcode struct and writes the Go code
    // snippets contained within it to the io.Writer, w, provided as an argument.
    // The output includes a package header which is generated.
    
    func write(ctx context.Context, code *gogen.GeneratedCode, path string, sbiType spb.Type) error {
    
    	if err := os.Mkdir(path, 0755); err != nil {
    
    		if err.(*fs.PathError).Err.Error() != "file exists" { //nolint:errorlint
    
    			return err
    		}
    	}
    	switch sbiType {
    	case spb.Type_TYPE_PLUGIN:
    		return writePlugin(code, path)
    	case spb.Type_TYPE_CONTAINERISED:
    		return writeCsbi(ctx, code, path)
    	default:
    		return fmt.Errorf("invalid sbi type provided")
    	}
    }
    
    func removePort(ip net.Addr) (string, error) {
    	addr, ok := ip.(*net.TCPAddr)
    	if !ok {
    		return "", fmt.Errorf("invalid type assertion")
    	}
    	return addr.IP.String(), nil
    }
    
    
    func writeCsbi(ctx context.Context, code *gogen.GeneratedCode, path string) error {
    
    	p, ok := peer.FromContext(ctx)
    	if !ok || p == nil {
    		e := fmt.Errorf("no peer information in context %v", ctx)
    		log.Error(e)
    		return status.Errorf(codes.Aborted, "%v", e)
    	}
    	controller, err := removePort(p.Addr)
    	if err != nil {
    		log.Error(err)
    		return status.Errorf(codes.Aborted, "%v", err)
    	}
    	target := ctx.Value("target-address")
    
    	writerViper := viper.New()
    	writerViper.Set("uuid", path)
    	writerViper.Set("controller", net.JoinHostPort(controller, "55055"))
    	writerViper.Set("target", target)
    
    	if err := writerViper.WriteConfigAs(filepath.Join(path, ".csbi.toml")); err != nil {
    		return err
    	}
    
    	if err := copyFile(path, "csbi.go"); err != nil {
    		return err
    	}
    
    
    	if err := copyFile(path, gostructAdditionsName); err != nil {
    		return err
    	}
    
    
    	if err := copyFile(path, "go.mod"); err != nil {
    		return err
    	}
    
    	if err := copyFile(path, "go.sum"); err != nil {
    		return err
    	}
    
    	if err := copyFile(path, "Dockerfile"); err != nil {
    		return err
    	}
    
    
    	return writeGoStruct(path, code, spb.Type_TYPE_CONTAINERISED)
    
    func writePlugin(code *gogen.GeneratedCode, path string) error {
    
    	if err := copyFile(path, gostructAdditionsName); err != nil {
    		return err
    	}
    
    	return writeGoStruct(path, code, spb.Type_TYPE_PLUGIN)
    
    }
    
    func copyFile(path, filename string) error {
    	var stderr bytes.Buffer
    	srcFile := filepath.Join("resources", filename)
    	dstFile := filepath.Join(path, filename)
    	cmd := exec.Command("cp", srcFile, dstFile)
    	cmd.Stderr = &stderr
    	err := cmd.Run()
    	if err != nil {
    		log.Error(stderr.String())
    		return err
    	}
    
    	log.WithFields(log.Fields{
    		"source file": srcFile,
    		"dst file":    dstFile,
    	}).Debugf("file copied")
    	return nil
    }
    
    
    func writeGoStruct(path string, code *gogen.GeneratedCode, t spb.Type) error {
    
    	file := filepath.Join(path, gostructName)
    	generatedCode := genutil.OpenFile(file)
    	defer genutil.SyncFile(generatedCode)
    
    	// Write the package header to the supplier writer.
    
    	fmt.Fprint(generatedCode, code.CommonHeader) //nolint:errcheck
    	fmt.Fprint(generatedCode, code.OneOffHeader) //nolint:errcheck
    
    
    	// Write the returned Go code out. First the Structs - which is the struct
    	// definitions for the generated YANG entity, followed by the enumerations.
    	for _, snippet := range code.Structs {
    
    		fmt.Fprintln(generatedCode, snippet.String()) //nolint:errcheck
    
    	}
    
    	for _, snippet := range code.Enums {
    
    		fmt.Fprintln(generatedCode, snippet) //nolint:errcheck
    
    	}
    
    	// Write the generated enumeration map out.
    
    	fmt.Fprintln(generatedCode, code.EnumMap) //nolint:errcheck
    
    
    	// Write the schema out if it was received.
    	if len(code.JSONSchemaCode) > 0 {
    
    		fmt.Fprintln(generatedCode, code.JSONSchemaCode) //nolint:errcheck
    
    		fmt.Fprintln(generatedCode, code.EnumTypeMap) //nolint:errcheck
    
    	}
    
    	if err := writeManifest(path, &Manifest{
    		Name:    "basic",
    		Author:  "goSDN-Team",
    		Version: "v1.0.0",
    	}); err != nil {
    		return err
    	}
    
    	return nil
    }
    
    
    func writeCode(path string, code *gogen.GeneratedCode, t spb.Type) error {
    
    	code.CommonHeader = strings.TrimSuffix(code.CommonHeader, ")\n")
    	code.CommonHeader = code.CommonHeader + southboundImportAmendmend
    
    	if t == spb.Type_TYPE_CONTAINERISED {
    
    		sbiStructCopy.Methods = sbiStructCopy.Methods + southboundStructCsbiAmendmend
    
    		sbiStructCopy.Methods = sbiStructCopy.Methods + southboundStructPluginAmendmend
    
    	code.Structs = append(code.Structs, sbiStructCopy)
    
    	file := filepath.Join(path, gostructName)
    
    	generatedCode := genutil.OpenFile(file)
    	defer genutil.SyncFile(generatedCode)
    	// Write the package header to the supplier writer.
    
    	fmt.Fprint(generatedCode, code.CommonHeader) //nolint:errcheck
    	fmt.Fprint(generatedCode, code.OneOffHeader) //nolint:errcheck
    
    
    	// Write the returned Go code out. First the Structs - which is the struct
    	// definitions for the generated YANG entity, followed by the enumerations.
    	for _, snippet := range code.Structs {
    
    		fmt.Fprintln(generatedCode, snippet) //nolint:errcheck
    
    	}
    
    	for _, snippet := range code.Enums {
    
    		fmt.Fprintln(generatedCode, snippet) //nolint:errcheck
    
    	}
    
    	// Write the generated enumeration map out.
    
    	fmt.Fprintln(generatedCode, code.EnumMap) //nolint:errcheck
    
    
    	// Write the schema out if it was received.
    	if len(code.JSONSchemaCode) > 0 {
    
    		fmt.Fprintln(generatedCode, code.JSONSchemaCode) //nolint:errcheck
    
    	}
    
    	if len(code.EnumTypeMap) > 0 {
    
    		fmt.Fprintln(generatedCode, code.EnumTypeMap) //nolint:errcheck
    
    	}
    
    	if err := writeManifest(path, &Manifest{
    		Name:    "basic",
    		Author:  "goSDN-Team",
    		Version: "v1.0.0",
    	}); err != nil {
    		return err
    	}
    
    	return nil
    }
    
    
    // Manifest represents a csbi manifest.
    
    type Manifest struct {
    	Name, Author, Version string
    }
    
    func writeManifest(path string, manifest *Manifest) error {
    	m, err := yaml.Marshal(manifest)
    	if err != nil {
    		return err
    	}
    
    Andre Sterba's avatar
    Andre Sterba committed
    	if err := os.WriteFile(filepath.Join(path, manifestFileName), m, 0644); err != nil {