diff --git a/api/dataplane/v1/service/service_cluster_end.go b/api/dataplane/v1/service/cluster_service_end.go similarity index 70% rename from api/dataplane/v1/service/service_cluster_end.go rename to api/dataplane/v1/service/cluster_service_end.go index 1e97f5f..d152b0d 100644 --- a/api/dataplane/v1/service/service_cluster_end.go +++ b/api/dataplane/v1/service/cluster_service_end.go @@ -2,8 +2,11 @@ package service import ( "context" + "encoding/binary" + "encoding/json" "io" "net" + "strings" "sync" "time" @@ -11,6 +14,7 @@ import ( mapset "github.com/deckarep/golang-set/v2" clusterv1 "github.com/singchia/frontier/api/controlplane/frontlas/v1" + "github.com/singchia/frontier/pkg/frontier/apis" "github.com/singchia/frontier/pkg/mapmap" "github.com/singchia/geminio" "github.com/singchia/geminio/delegate" @@ -23,7 +27,7 @@ type frontierNend struct { end *serviceEnd } -type serviceClusterEnd struct { +type clusterServiceEnd struct { *delegate.UnimplementedDelegate cc clusterv1.ClusterServiceClient @@ -46,14 +50,14 @@ type serviceClusterEnd struct { closed chan struct{} } -func newServiceClusterEnd(addr string, opts ...ServiceOption) (*serviceClusterEnd, error) { +func newclusterServiceEnd(addr string, opts ...ServiceOption) (*clusterServiceEnd, error) { conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { return nil, err } cc := clusterv1.NewClusterServiceClient(conn) - end := &serviceClusterEnd{ + end := &clusterServiceEnd{ cc: cc, serviceOption: &serviceOption{}, rpcs: map[string]geminio.RPC{}, @@ -73,7 +77,7 @@ func newServiceClusterEnd(addr string, opts ...ServiceOption) (*serviceClusterEn return end, nil } -func (end *serviceClusterEnd) start() { +func (end *clusterServiceEnd) start() { ticker := time.NewTicker(10 * time.Second) defer ticker.Stop() for { @@ -90,7 +94,7 @@ func (end *serviceClusterEnd) start() { } } -func (end *serviceClusterEnd) clear(frontierID string) { +func (end *clusterServiceEnd) clear(frontierID string) { end.updating.Lock() defer end.updating.Unlock() @@ -102,7 +106,7 @@ func (end *serviceClusterEnd) clear(frontierID string) { end.edgefrontiers.DelValue(frontierID) } -func (end *serviceClusterEnd) update() error { +func (end *clusterServiceEnd) update() error { rsp, err := end.cc.ListFrontiers(context.TODO(), &clusterv1.ListFrontiersRequest{}) if err != nil { end.logger.Errorf("list frontiers err: %s", err) @@ -166,7 +170,7 @@ FOUND: return nil } -func (end *serviceClusterEnd) lookup(edgeID uint64) (string, *serviceEnd, error) { +func (end *clusterServiceEnd) lookup(edgeID uint64) (string, *serviceEnd, error) { var ( frontier *clusterv1.Frontier serviceEnd *serviceEnd @@ -207,7 +211,7 @@ func (end *serviceClusterEnd) lookup(edgeID uint64) (string, *serviceEnd, error) return frontierID.(string), serviceEnd, nil } -func (end *serviceClusterEnd) pickone() *serviceEnd { +func (end *clusterServiceEnd) pickone() *serviceEnd { var serviceEnd *serviceEnd end.frontiers.Range(func(_, value interface{}) bool { // return first one @@ -222,7 +226,7 @@ func frontierEqual(a, b *clusterv1.Frontier) bool { a.FrontierId == b.FrontierId } -func (service *serviceClusterEnd) newServiceEnd(addr string) (*serviceEnd, error) { +func (service *clusterServiceEnd) newServiceEnd(addr string) (*serviceEnd, error) { dialer := func() (net.Conn, error) { return net.Dial("tcp", addr) } @@ -271,7 +275,7 @@ ERR: } // multiplexer -func (end *serviceClusterEnd) AcceptStream() (geminio.Stream, error) { +func (end *clusterServiceEnd) AcceptStream() (geminio.Stream, error) { st, ok := <-end.acceptStreamCh if !ok { return nil, io.EOF @@ -279,7 +283,7 @@ func (end *serviceClusterEnd) AcceptStream() (geminio.Stream, error) { return st, nil } -func (end *serviceClusterEnd) OpenStream(ctx context.Context, edgeID uint64) (geminio.Stream, error) { +func (end *clusterServiceEnd) OpenStream(ctx context.Context, edgeID uint64) (geminio.Stream, error) { frontierID, serviceEnd, err := end.lookup(edgeID) if err != nil { return nil, err @@ -292,7 +296,7 @@ func (end *serviceClusterEnd) OpenStream(ctx context.Context, edgeID uint64) (ge return stream, nil } -func (end *serviceClusterEnd) ListStreams() []geminio.Stream { +func (end *clusterServiceEnd) ListStreams() []geminio.Stream { streams := []geminio.Stream{} end.frontiers.Range(func(_, value interface{}) bool { sts := value.(*frontierNend).end.ListStreams() @@ -305,7 +309,7 @@ func (end *serviceClusterEnd) ListStreams() []geminio.Stream { } // Messager -func (end *serviceClusterEnd) NewMessage(data []byte) geminio.Message { +func (end *clusterServiceEnd) NewMessage(data []byte) geminio.Message { serviceEnd := end.pickone() if serviceEnd == nil { return nil @@ -313,7 +317,7 @@ func (end *serviceClusterEnd) NewMessage(data []byte) geminio.Message { return serviceEnd.NewMessage(data) } -func (end *serviceClusterEnd) Publish(ctx context.Context, edgeID uint64, msg geminio.Message) error { +func (end *clusterServiceEnd) Publish(ctx context.Context, edgeID uint64, msg geminio.Message) error { fronterID, serviceEnd, err := end.lookup(edgeID) if err != nil { return err @@ -326,7 +330,7 @@ func (end *serviceClusterEnd) Publish(ctx context.Context, edgeID uint64, msg ge return nil } -func (end *serviceClusterEnd) PublishAsync(ctx context.Context, edgeID uint64, msg geminio.Message, ch chan *geminio.Publish) (*geminio.Publish, error) { +func (end *clusterServiceEnd) PublishAsync(ctx context.Context, edgeID uint64, msg geminio.Message, ch chan *geminio.Publish) (*geminio.Publish, error) { fronterID, serviceEnd, err := end.lookup(edgeID) if err != nil { return nil, err @@ -339,7 +343,7 @@ func (end *serviceClusterEnd) PublishAsync(ctx context.Context, edgeID uint64, m return pub, err } -func (end *serviceClusterEnd) Receive(ctx context.Context) (geminio.Message, error) { +func (end *clusterServiceEnd) Receive(ctx context.Context) (geminio.Message, error) { msg, ok := <-end.acceptMsgCh if !ok { return nil, io.EOF @@ -348,7 +352,7 @@ func (end *serviceClusterEnd) Receive(ctx context.Context) (geminio.Message, err } // RPCer -func (end *serviceClusterEnd) NewRequest(data []byte) geminio.Request { +func (end *clusterServiceEnd) NewRequest(data []byte) geminio.Request { serviceEnd := end.pickone() if serviceEnd == nil { return nil @@ -356,7 +360,7 @@ func (end *serviceClusterEnd) NewRequest(data []byte) geminio.Request { return serviceEnd.NewRequest(data) } -func (end *serviceClusterEnd) Call(ctx context.Context, edgeID uint64, method string, req geminio.Request) (geminio.Response, error) { +func (end *clusterServiceEnd) Call(ctx context.Context, edgeID uint64, method string, req geminio.Request) (geminio.Response, error) { fronterID, serviceEnd, err := end.lookup(edgeID) if err != nil { return nil, err @@ -369,7 +373,7 @@ func (end *serviceClusterEnd) Call(ctx context.Context, edgeID uint64, method st return rsp, nil } -func (end *serviceClusterEnd) CallAsync(ctx context.Context, edgeID uint64, method string, req geminio.Request, ch chan *geminio.Call) (*geminio.Call, error) { +func (end *clusterServiceEnd) CallAsync(ctx context.Context, edgeID uint64, method string, req geminio.Request, ch chan *geminio.Call) (*geminio.Call, error) { fronterID, serviceEnd, err := end.lookup(edgeID) if err != nil { return nil, err @@ -382,7 +386,7 @@ func (end *serviceClusterEnd) CallAsync(ctx context.Context, edgeID uint64, meth return call, nil } -func (end *serviceClusterEnd) Register(ctx context.Context, method string, rpc geminio.RPC) error { +func (end *clusterServiceEnd) Register(ctx context.Context, method string, rpc geminio.RPC) error { end.appMtx.Lock() end.rpcs[method] = rpc end.appMtx.Unlock() @@ -401,8 +405,35 @@ func (end *serviceClusterEnd) Register(ctx context.Context, method string, rpc g return err } +// net.Listener +func (end *clusterServiceEnd) Accept() (net.Conn, error) { + st, ok := <-end.acceptStreamCh + if !ok { + return nil, io.EOF + } + return st, nil +} + +func (end *clusterServiceEnd) Network() string { + return "tcp" +} + +func (end *clusterServiceEnd) String() string { + addrs := []string{} + end.frontiers.Range(func(key, value interface{}) bool { + addr := value.(*frontierNend).end.Addr().String() + addrs = append(addrs, addr) + return true + }) + return strings.Join(addrs, ";") +} + +func (end *clusterServiceEnd) Addr() net.Addr { + return end +} + // close -func (end *serviceClusterEnd) Close() error { +func (end *clusterServiceEnd) Close() error { close(end.closed) close(end.acceptMsgCh) close(end.acceptStreamCh) @@ -419,3 +450,58 @@ func (end *serviceClusterEnd) Close() error { }) return err } + +// Control Register +func (end *clusterServiceEnd) RegisterGetEdgeID(ctx context.Context, getEdgeID GetEdgeID) error { + // we call Register of clusterServiceEnd because we need rpcs record getEdgeID + // and also Register getEdgeID to all frontier + return end.Register(ctx, apis.RPCGetEdgeID, func(ctx context.Context, req geminio.Request, rsp geminio.Response) { + id, err := getEdgeID(req.Data()) + if err != nil { + // we just deliver the err back + // get ID err will force close the edge unless EdgeIDAllocWhenNoIDServiceOn is configured + rsp.SetError(err) + return + } + hex := make([]byte, 8) + binary.BigEndian.PutUint64(hex, id) + rsp.SetData(hex) + }) +} + +func (end *clusterServiceEnd) RegisterEdgeOnline(ctx context.Context, edgeOnline EdgeOnline) error { + return end.Register( + ctx, apis.RPCEdgeOnline, func(ctx context.Context, req geminio.Request, rsp geminio.Response) { + on := &apis.OnEdgeOnline{} + err := json.Unmarshal(req.Data(), on) + if err != nil { + // shouldn't be here + rsp.SetError(err) + return + } + err = edgeOnline(on.EdgeID, on.Meta, on) + if err != nil { + // online err will force close the edge + rsp.SetError(err) + return + } + // if allowed, the edge will continue the connection + }) +} + +func (end *clusterServiceEnd) RegisterEdgeOffline(ctx context.Context, edgeOffline EdgeOffline) error { + return end.Register(ctx, apis.RPCEdgeOffline, func(ctx context.Context, req geminio.Request, rsp geminio.Response) { + off := &apis.OnEdgeOffline{} + err := json.Unmarshal(req.Data(), off) + if err != nil { + // shouldn't be here + rsp.SetError(err) + return + } + err = edgeOffline(off.EdgeID, off.Meta, off) + if err != nil { + rsp.SetError(err) + return + } + }) +} diff --git a/api/dataplane/v1/service/service.go b/api/dataplane/v1/service/service.go index ed7bf0e..c08015a 100644 --- a/api/dataplane/v1/service/service.go +++ b/api/dataplane/v1/service/service.go @@ -73,5 +73,10 @@ type Dialer func() (net.Conn, error) // the service field specific the role for this Service, and then Edge can OpenStream to this service func NewService(dialer Dialer, opts ...ServiceOption) (Service, error) { - return newServiceEnd(client.Dialer(dialer), opts...) + return newRetryServiceEnd(client.Dialer(dialer), opts...) +} + +// call this function when you deploy a frontier cluster +func NewClusterService(frontlasAddr string, opts ...ServiceOption) (Service, error) { + return newclusterServiceEnd(frontlasAddr, opts...) } diff --git a/api/dataplane/v1/service/service_end.go b/api/dataplane/v1/service/service_end.go index 3964fdf..7ab7ad1 100644 --- a/api/dataplane/v1/service/service_end.go +++ b/api/dataplane/v1/service/service_end.go @@ -17,6 +17,45 @@ type serviceEnd struct { } func newServiceEnd(dialer client.Dialer, opts ...ServiceOption) (*serviceEnd, error) { + // options + sopt := &serviceOption{} + for _, opt := range opts { + opt(sopt) + } + sopts := &client.EndOptions{} + if sopt.tmr != nil { + sopts.SetTimer(sopt.tmr) + } + if sopt.logger != nil { + sopts.SetLog(sopt.logger) + } + // meta + meta := &apis.Meta{} + if sopt.topics != nil { + // we deliver topics in meta + meta.Topics = sopt.topics + } + if sopt.service != "" { + meta.Service = sopt.service + } + data, err := json.Marshal(meta) + if err != nil { + return nil, err + } + sopts.SetMeta(data) + // delegate + if sopt.delegate != nil { + sopts.SetDelegate(sopt.delegate) + } + // new geminio end + end, err := client.NewEndWithDialer(dialer, sopts) + if err != nil { + return nil, err + } + return &serviceEnd{end}, nil +} + +func newRetryServiceEnd(dialer client.Dialer, opts ...ServiceOption) (*serviceEnd, error) { // options sopt := &serviceOption{} for _, opt := range opts { diff --git a/etc/frontier.yaml b/etc/frontier.yaml new file mode 100644 index 0000000..61f614f --- /dev/null +++ b/etc/frontier.yaml @@ -0,0 +1,134 @@ +daemon: + rlimit: + enable: false + nofile: 102400 + pprof: + enable: true + addr: 0.0.0.0:6060 + cpu_profile_rate: 0 +edgebound: + listen: + network: tcp + addr: 0.0.0.0:30012 + tls: + enable: false + mtls: false + ca_certs: + - ca1.cert + - ca2.cert + certs: + - cert: edgebound.cert + key: edgebound.key + insecure_skip_verify: false + bypass: + network: tcp + addr: 192.168.1.10:8443 + tls: + enable: true + mtls: true + ca_certs: + - ca1.cert + certs: + - cert: frontier.cert + key: frontier.key + insecure_skip_verify: false + bypass_enable: false + edgeid_alloc_when_no_idservice_on: true +servicebound: + listen: + network: tcp + addr: 0.0.0.0:30011 + tls: + enable: false + mtls: false + ca_certs: + - ca1.cert + - ca2.cert + certs: + - cert: servicebound.cert + key: servicebound.key + insecure_skip_verify: false +controlplane: + enable: false + listen: + network: tcp + addr: 0.0.0.0:30010 +dao: + debug: false +frontlas: + enable: false + dial: + network: tcp + addr: 127.0.0.1:30021 + metrics: + enable: false + interval: 0 +mqm: + kafka: + enable: false + addrs: [] + producer: + topics: [] + async: false + maxmessagebytes: 0 + requiredacks: 0 + timeout: 0 + compression: none + compressionlevel: 0 + idempotent: false + flush: + bytes: 0 + messages: 0 + frequency: 0 + maxmessages: 0 + retry: + max: 0 + backoff: 0 + amqp: + enable: false + addrs: [] + vhost: "" + channelmax: 0 + framesize: 0 + heartbeat: 0 + locale: "" + exchanges: [] + queues: [] + queuebindings: [] + producer: + routingkeys: [] + exchange: "" + mandatory: false + immediate: false + headers: {} + contenttype: "" + contentencoding: "" + deliverymode: 0 + priority: 0 + replyto: "" + expiration: "" + type: "" + userid: "" + appid: "" + nats: + enable: false + addrs: [] + producer: + subjects: [] + jetstream: + enable: false + name: "" + producer: + subjects: [] + nsq: + enable: false + addrs: [] + producer: + topics: [] + redis: + enable: false + addrs: [] + db: 0 + password: "" + producer: + channels: [] diff --git a/etc/frontlas.yaml b/etc/frontlas.yaml new file mode 100644 index 0000000..7ec98da --- /dev/null +++ b/etc/frontlas.yaml @@ -0,0 +1,95 @@ +daemon: + rlimit: + enable: false + nofile: 1024 + pprof: + enable: true + addr: 0.0.0.0:6061 + cpu_profile_rate: 0 +control_plane: + listen: + network: tcp + addr: 0.0.0.0:30020 +frontier_plane: + listen: + network: tcp + addr: 0.0.0.0:30021 +redis: + mode: "" + standalone: + network: "" + addr: "" + protocol: 0 + username: "" + password: "" + db: 0 + clientname: "" + maxretries: 0 + minretrybackoff: 0 + maxretrybackoff: 0 + dialtimeout: 0 + readtimeout: 0 + writetimeout: 0 + poolfifo: false + poolsize: 0 + pooltimeout: 0 + minidleconns: 0 + maxidleconns: 0 + maxactiveconns: 0 + connmaxidletime: 0 + connmaxlifetime: 0 + disableindentity: false + identitysuffix: "" + sentinel: + addrs: [] + master_name: "" + protocol: 0 + username: "" + password: "" + db: 0 + clientname: "" + routebylatency: false + routerandomly: false + replicaonly: false + usedisconnectedreplicas: false + maxretries: 0 + minretrybackoff: 0 + maxretrybackoff: 0 + dialtimeout: 0 + readtimeout: 0 + writetimeout: 0 + poolfifo: false + poolsize: 0 + pooltimeout: 0 + minidleconns: 0 + maxidleconns: 0 + maxactiveconns: 0 + connmaxidletime: 0 + connmaxlifetime: 0 + disableindentity: false + identitysuffix: "" + cluster: + addrs: [] + protocol: 0 + username: "" + password: "" + clientname: "" + maxredirects: 0 + routebylatency: false + routerandomly: false + maxretries: 0 + minretrybackoff: 0 + maxretrybackoff: 0 + dialtimeout: 0 + readtimeout: 0 + writetimeout: 0 + poolfifo: false + poolsize: 0 + pooltimeout: 0 + minidleconns: 0 + maxidleconns: 0 + maxactiveconns: 0 + connmaxidletime: 0 + connmaxlifetime: 0 + disableindentity: false + identitysuffix: "" diff --git a/pkg/config/config.go b/pkg/config/config.go index 5d1ab3f..8c7a56d 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -18,12 +18,12 @@ type Listen struct { Network string `yaml:"network"` Addr string `yaml:"addr"` AdvertisedAddr string `yaml:"advertised_addr,omitempty"` - TLS TLS `yaml:"tls"` + TLS TLS `yaml:"tls,omitempty"` } type Dial struct { Network string `yaml:"network"` Addr string `yaml:"addr"` AdvertisedAddr string `yaml:"advertised_addr,omitempty"` - TLS TLS `yaml:"tls"` + TLS TLS `yaml:"tls,omitempty"` } diff --git a/pkg/frontier/config/config.go b/pkg/frontier/config/config.go index 8bd58c9..817f7d7 100644 --- a/pkg/frontier/config/config.go +++ b/pkg/frontier/config/config.go @@ -56,6 +56,7 @@ type Servicebound struct { } type ControlPlane struct { + Enable bool `yaml:"enable"` Listen config.Listen `yaml:"listen"` } @@ -337,23 +338,25 @@ func genDefaultConfig(writer io.Writer) error { conf := &Configuration{ Daemon: Daemon{ RLimit: RLimit{ - NumFile: 1024, + NumFile: 102400, }, PProf: PProf{ Enable: true, Addr: "0.0.0.0:6060", }, }, + // default listen on 30010 ControlPlane: ControlPlane{ Listen: config.Listen{ Network: "tcp", - Addr: "0.0.0.0:2430", + Addr: "0.0.0.0:30010", }, }, + // default listen on 30011 Servicebound: Servicebound{ Listen: config.Listen{ Network: "tcp", - Addr: "0.0.0.0:2431", + Addr: "0.0.0.0:30011", TLS: config.TLS{ Enable: false, MTLS: false, @@ -370,10 +373,11 @@ func genDefaultConfig(writer io.Writer) error { }, }, }, + // default listen on 30012 Edgebound: Edgebound{ Listen: config.Listen{ Network: "tcp", - Addr: "0.0.0.0:2432", + Addr: "0.0.0.0:30012", TLS: config.TLS{ Enable: false, MTLS: false, @@ -412,6 +416,34 @@ func genDefaultConfig(writer io.Writer) error { Dao: Dao{ Debug: false, }, + Frontlas: Frontlas{ + Enable: false, + Dial: config.Dial{ + Network: "tcp", + Addr: "127.0.0.1:30021", + TLS: config.TLS{ + Enable: false, + MTLS: false, + }, + }, + }, + MQM: MQM{ + Kafka: Kafka{ + Enable: false, + }, + AMQP: AMQP{ + Enable: false, + }, + Nats: Nats{ + Enable: false, + }, + NSQ: NSQ{ + Enable: false, + }, + Redis: Redis{ + Enable: false, + }, + }, } data, err := yaml.Marshal(conf) if err != nil { diff --git a/pkg/frontier/config/config_test.go b/pkg/frontier/config/config_test.go index bea213d..458e19e 100644 --- a/pkg/frontier/config/config_test.go +++ b/pkg/frontier/config/config_test.go @@ -61,7 +61,7 @@ func TestParseFile(t *testing.T) { } func TestGenDefaultConfig(t *testing.T) { - file, err := os.OpenFile("./config.yaml", os.O_CREATE|os.O_RDWR, 0666) + file, err := os.OpenFile("../../../etc/frontier.yaml", os.O_CREATE|os.O_RDWR, 0666) if err != nil { t.Error(err) } diff --git a/pkg/frontier/config/frontier.yaml b/pkg/frontier/config/frontier.yaml deleted file mode 100644 index 6089716..0000000 --- a/pkg/frontier/config/frontier.yaml +++ /dev/null @@ -1,62 +0,0 @@ -daemon: - rlimit: - enable: false - nofile: 1024 - pprof: - enable: true - addr: 0.0.0.0:6060 - cpu_profile_rate: 0 -edgebound: - listen: - network: tcp - addr: 0.0.0.0:2432 - tls: - enable: false - mtls: false - ca_certs: - - ca1.cert - - ca2.cert - certs: - - cert: edgebound.cert - key: edgebound.key - insecure_skip_verify: false - bypass: - enable: false - network: tcp - addr: 192.168.1.10:8443 - tls: - enable: true - mtls: true - ca_certs: - - ca1.cert - certs: - - cert: frontier.cert - key: frontier.key - insecure_skip_verify: false - edgeid_alloc_when_no_idservice_on: true -servicebound: - listen: - network: tcp - addr: 0.0.0.0:2431 - tls: - enable: false - mtls: false - ca_certs: - - ca1.cert - - ca2.cert - certs: - - cert: servicebound.cert - key: servicebound.key - insecure_skip_verify: false -controlplane: - listen: - network: tcp - addr: 0.0.0.0:2430 - tls: - enable: false - mtls: false - ca_certs: [] - certs: [] - insecure_skip_verify: false -dao: - debug: false diff --git a/pkg/frontlas/config/config.go b/pkg/frontlas/config/config.go index 8b42ef8..9d9e4b7 100644 --- a/pkg/frontlas/config/config.go +++ b/pkg/frontlas/config/config.go @@ -3,8 +3,10 @@ package config import ( "flag" "fmt" + "io" "os" + armio "github.com/jumboframes/armorigo/io" "github.com/jumboframes/armorigo/log" "github.com/singchia/frontier/pkg/config" "github.com/spf13/pflag" @@ -254,3 +256,39 @@ func Parse() (*Configuration, error) { } return config, nil } + +func genDefaultConfig(writer io.Writer) error { + conf := &Configuration{ + Daemon: Daemon{ + RLimit: RLimit{ + NumFile: 1024, + }, + PProf: PProf{ + Enable: true, + Addr: "0.0.0.0:6061", + }, + }, + ControlPlane: ControlPlane{ + Listen: config.Listen{ + Network: "tcp", + Addr: "0.0.0.0:30020", + }, + }, + FrontierManager: FrontierManager{ + Listen: config.Listen{ + Network: "tcp", + Addr: "0.0.0.0:30021", + }, + }, + Redis: Redis{}, + } + data, err := yaml.Marshal(conf) + if err != nil { + return err + } + _, err = armio.WriteAll(data, writer) + if err != nil { + return err + } + return nil +} diff --git a/pkg/frontlas/config/config_test.go b/pkg/frontlas/config/config_test.go new file mode 100644 index 0000000..b707560 --- /dev/null +++ b/pkg/frontlas/config/config_test.go @@ -0,0 +1,18 @@ +package config + +import ( + "os" + "testing" +) + +func TestGenDefaultConfig(t *testing.T) { + file, err := os.OpenFile("../../../etc/frontlas.yaml", os.O_CREATE|os.O_RDWR, 0666) + if err != nil { + t.Error(err) + } + defer file.Close() + err = genDefaultConfig(file) + if err != nil { + t.Error(err) + } +}