/* * Copyright (C) 2017-2018 "IoT.bzh" * Author Sebastien Douheret * * 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. */ package agent import ( "fmt" "io" "strings" "time" common "gerrit.automotivelinux.org/gerrit/src/xds/xds-common.git" uuid "github.com/satori/go.uuid" ) // XdsSupervisor . type XdsSupervisor struct { *Context ID string BaseURL string ConnRetry int Connected bool Disabled bool // Private fields client *common.HTTPClient logOut io.Writer cbOnConnect OnConnectedXdsSupervCB } // XdsSuperVRequest Resquest field of a reply type XdsSuperVRequest struct { Status string `json:"status"` Info string `json:"info"` } // XdsSuperVReply Reply structure of XDS Supervision Daemon type XdsSuperVReply struct { JType string `json:"jtype"` Request XdsSuperVRequest `json:"request"` Response interface{} `json:"response"` } // XdsSuperVTraceConfig type XdsSuperVTraceConfig struct { Pid int `json:"pid"` Pids []int `json:"pids"` WsName string `json:"ws"` } // OnConnectedXdsSupervCB connect callback type OnConnectedXdsSupervCB func(svr *XdsSupervisor) error // NewXdsSupervisor creates an instance of XdsSupervisor func NewXdsSupervisor(ctx *Context) *XdsSupervisor { return &XdsSupervisor{ Context: ctx, ID: "XdsSupervisor-" + uuid.NewV1().String(), BaseURL: ctx.Config.FileConf.ProfileConf.XDSBinder.URL, ConnRetry: ctx.Config.FileConf.ProfileConf.XDSBinder.ConnRetry, Connected: false, Disabled: false, logOut: ctx.Log.Out, } } // Connect Establish HTTP connection with XDS Supervisor Dameon func (xs *XdsSupervisor) Connect() error { var err error var retry int xs.Disabled = false xs.Connected = false err = nil for retry = xs.ConnRetry; retry > 0; retry-- { if err = xs._CreateConnectHTTP(); err == nil { break } if retry == xs.ConnRetry { // Notify only on the first conn error // doing that avoid 2 notifs (conn false; conn true) on startup xs._NotifyState() } xs.Log.Infof("Establishing connection to XDS Supervisor daemon (retry %d/%d)", retry, xs.ConnRetry) time.Sleep(time.Second) } if retry == 0 { // FIXME: re-use _Reconnect to wait longer in background return fmt.Errorf("Connection to XDS Supervisor daemon failure") } if err != nil { return err } // Check HTTP connection and establish WS connection err = xs._Connect(false) return err } // ConnectOn Register a callback on events reception func (xs *XdsSupervisor) ConnectOn(f OnConnectedXdsSupervCB) error { xs.cbOnConnect = f return nil } // GetVersion Send Get request to retrieve XDS Supervision version func (xs *XdsSupervisor) GetVersion(res interface{}) error { // FIXME add suffix URLSuffix in common HTTP client lib instead of _BuildURL return xs.client.Get(xs._BuildURL("/version"), &res) } // GetTopo Send Get request to retrieve Services/Daemons topology func (xs *XdsSupervisor) GetTopo(res interface{}) error { return xs.client.Get(xs._BuildURL("/list"), &res) } // StartTrace Send Supervisor config and start tracing func (xs *XdsSupervisor) StartTrace(cfg XdsSuperVTraceConfig, res interface{}) error { return xs.client.Post(xs._BuildURL("/trace/start"), cfg, &res) } // StopTrace Send Supervisor stop tracing func (xs *XdsSupervisor) StopTrace(res interface{}) error { var cfg interface{} return xs.client.Post(xs._BuildURL("/trace/stop"), cfg, res) } /*** ** Private functions ***/ // _BuildURL . func (xs *XdsSupervisor) _BuildURL(url string) string { return url + "?token=HELLO&uuid=magic" } // Create HTTP client func (xs *XdsSupervisor) _CreateConnectHTTP() error { var err error // FIXME SEB - Client key not in header but in cookie // temporary workaround: used _BuildURL to append uuid=magic in URL // map[Set-Cookie:[x-afb-uuid-5678=2b185cc3-276b-4097-91fa-d607eaf937e6; Path=/api; Max-Age=32000000; ... //port := strings.Split(xs.BaseURL, ":")[2] //"x-afb-uuid-" + port xs.client, err = common.HTTPNewClient(xs.BaseURL, common.HTTPClientConfig{ //HeaderClientKeyName: "Xds-Sid", HeaderAPIKeyName: "token", Apikey: "HELLO", URLPrefix: "/api/xds", CsrfDisable: true, LogOut: xs.logOut, LogPrefix: "XDSSUPERV: ", LogLevel: common.HTTPLogLevelWarning, }) xs.client.SetLogLevel(xs.Log.Level.String()) if err != nil { msg := ": " + err.Error() if strings.Contains(err.Error(), "connection refused") { msg = fmt.Sprintf("(url: %s)", xs.BaseURL) } return fmt.Errorf("ERROR: cannot connect to XDS Supervisor %s", msg) } if xs.client == nil { return fmt.Errorf("ERROR: cannot connect to XDS Supervisor (null client)") } return nil } // _Connect Established HTTP and WS connection func (xs *XdsSupervisor) _Connect(reConn bool) error { var res interface{} if err := xs.client.Get(xs._BuildURL("/ping"), &res); err != nil { xs.Connected = false if !reConn { xs._NotifyState() } return err } xs.Connected = true // Call OnConnect callback if xs.cbOnConnect != nil { xs.cbOnConnect(xs) } xs._NotifyState() return nil } // _NotifyState Send event to notify changes func (xs *XdsSupervisor) _NotifyState() { /* TODO evSts := xaapiv1.ServerCfg{ ID: xs.ID, URL: xs.BaseURL, APIURL: xs.APIURL, PartialURL: xs.PartialURL, ConnRetry: xs.ConnRetry, Connected: xs.Connected, } if err := xs.events.Emit(xaapiv1.EVTServerConfig, evSts, ""); err != nil { xs.Log.Warningf("Cannot notify XdsServer state change: %v", err) } */ }