Jan Dittberner
91d4f69a9b
This commit adds the project setup and implements a basic signer client that sends health check commands to the signer.
166 lines
3.4 KiB
Go
166 lines
3.4 KiB
Go
/*
|
|
Copyright 2022 CAcert Inc.
|
|
SPDX-License-Identifier: Apache-2.0
|
|
|
|
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 client
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/tarm/serial"
|
|
|
|
"git.cacert.org/cacert-gosigner/pkg/messages"
|
|
"git.cacert.org/cacert-gosigner/pkg/protocol"
|
|
"git.cacert.org/cacert-gosignerclient/internal/config"
|
|
)
|
|
|
|
type Client struct {
|
|
port *serial.Port
|
|
logger *logrus.Logger
|
|
framer protocol.Framer
|
|
in chan []byte
|
|
out chan []byte
|
|
commands chan *protocol.Command
|
|
handler protocol.ClientHandler
|
|
config *config.ClientConfig
|
|
}
|
|
|
|
func (c *Client) Run(ctx context.Context) error {
|
|
protocolErrors := make(chan error)
|
|
framerErrors := make(chan error)
|
|
|
|
go func(f protocol.Framer) {
|
|
framerErrors <- f.ReadFrames(c.port, c.in)
|
|
}(c.framer)
|
|
|
|
go func(f protocol.Framer) {
|
|
framerErrors <- f.WriteFrames(c.port, c.out)
|
|
}(c.framer)
|
|
|
|
go func() {
|
|
clientProtocol := protocol.NewClient(c.handler, c.commands, c.in, c.out, c.logger)
|
|
|
|
protocolErrors <- clientProtocol.Handle()
|
|
}()
|
|
|
|
ctx, cancelCommandLoop := context.WithCancel(ctx)
|
|
|
|
go c.commandLoop(ctx)
|
|
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
cancelCommandLoop()
|
|
|
|
return nil
|
|
case err := <-framerErrors:
|
|
cancelCommandLoop()
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("error from framer: %w", err)
|
|
}
|
|
|
|
return nil
|
|
case err := <-protocolErrors:
|
|
cancelCommandLoop()
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("error from protocol: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *Client) setupConnection(serialConfig *serial.Config) error {
|
|
s, err := serial.OpenPort(serialConfig)
|
|
if err != nil {
|
|
return fmt.Errorf("could not open serial port: %w", err)
|
|
}
|
|
|
|
c.port = s
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *Client) Close() error {
|
|
close(c.in)
|
|
close(c.out)
|
|
|
|
if c.port != nil {
|
|
err := c.port.Close()
|
|
if err != nil {
|
|
return fmt.Errorf("could not close serial port: %w", err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *Client) commandLoop(ctx context.Context) {
|
|
healthTimer := time.NewTimer(c.config.HealthStart)
|
|
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-healthTimer.C:
|
|
announce, err := messages.BuildCommandAnnounce(messages.CmdHealth)
|
|
if err != nil {
|
|
c.logger.WithError(err).Error("could not build health command announce")
|
|
}
|
|
|
|
c.commands <- &protocol.Command{
|
|
Announce: announce,
|
|
Command: &messages.HealthCommand{},
|
|
}
|
|
|
|
healthTimer.Reset(c.config.HealthInterval)
|
|
}
|
|
}
|
|
}
|
|
|
|
func New(
|
|
cfg *config.ClientConfig,
|
|
logger *logrus.Logger,
|
|
handler protocol.ClientHandler,
|
|
commands chan *protocol.Command,
|
|
) (*Client, error) {
|
|
client := &Client{
|
|
logger: logger,
|
|
framer: protocol.NewCOBSFramer(logger),
|
|
in: make(chan []byte),
|
|
out: make(chan []byte),
|
|
commands: commands,
|
|
handler: handler,
|
|
config: cfg,
|
|
}
|
|
|
|
err := client.setupConnection(&serial.Config{
|
|
Name: cfg.Serial.Device,
|
|
Baud: cfg.Serial.Baud,
|
|
ReadTimeout: cfg.Serial.Timeout,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return client, nil
|
|
}
|