/
parklog.go
147 lines (124 loc) · 2.71 KB
/
parklog.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package main
import (
"crypto/tls"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net"
"net/url"
"os"
"time"
)
const DIAL_TIMEOUT = 5 * time.Second
type StreamStatus int
const (
CONNECTED StreamStatus = iota
CONNECTING
NOT_CONNECTED
)
type Streams []*Stream
func (streams *Streams) CloseAll() {
for _, stream := range *streams {
stream.Close()
}
}
func (streams *Streams) WriteAll(line string) (errors []error) {
for _, stream := range *streams {
if err := stream.Write(line); err != nil {
errors = append(errors, err)
}
}
return
}
type StreamConfig struct {
Url string `json:"url"`
Prefix string `json:"prefix"`
AllowSSCert bool `json:"allow_self_signed_cert"`
}
type Stream struct {
Url *url.URL
Conn io.WriteCloser
Conf *StreamConfig
Status StreamStatus
}
func NewStream(conf *StreamConfig) (*Stream, error) {
u, err := url.Parse(conf.Url)
if err != nil {
return nil, err
}
stream := &Stream{Url: u, Conf: conf, Status: CONNECTING}
stream.TryConnect()
return stream, nil
}
func (s *Stream) TryConnect() error {
s.Status = CONNECTING
if err := s.Connect(); err != nil {
s.Status = NOT_CONNECTED
return err
}
s.Status = CONNECTED
return nil
}
func (s *Stream) Connect() error {
var conn io.WriteCloser
var err error
path := s.Url.Host + s.Url.Path
switch {
case s.Url.Scheme == "tls" || s.Url.Scheme == "ssl":
tcpConn, dialErr := net.DialTimeout("tcp", path, DIAL_TIMEOUT)
if dialErr != nil {
config := &tls.Config{InsecureSkipVerify: s.Conf.AllowSSCert}
conn = tls.Client(tcpConn, config)
} else {
err = dialErr
}
case s.Url.Scheme == "file":
conn, err = os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0600)
default:
conn, err = net.DialTimeout(s.Url.Scheme, path, DIAL_TIMEOUT)
}
s.Conn = conn
return err
}
func (s *Stream) Write(line string) error {
switch {
case s.Status == CONNECTED:
toWrite := []byte(s.Conf.Prefix + line)
n, err := s.Conn.Write(toWrite)
if err != nil {
s.Status = NOT_CONNECTED
return err
}
if n != len(toWrite) {
return fmt.Errorf("Failed to write some bytes on %v", s.Url)
}
case s.Status == NOT_CONNECTED:
s.TryConnect()
}
return nil
}
func (s *Stream) Close() {
s.Conn.Close()
s.Status = NOT_CONNECTED
}
func InitStreams(configPath string) (error, Streams) {
var streamConfigs []StreamConfig
file, err := ioutil.ReadFile(configPath)
if err != nil {
return err, nil
}
confs := os.ExpandEnv(string(file))
if err = json.Unmarshal([]byte(confs), &streamConfigs); err != nil {
return err, nil
}
var streams Streams
for _, conf := range streamConfigs {
s, err := NewStream(&conf)
if err != nil {
return err, nil
}
streams = append(streams, s)
}
return nil, streams
}