forked from crossplane/function-sdk-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
sdk.go
149 lines (126 loc) · 4.32 KB
/
sdk.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
148
149
/*
Copyright 2023 The Crossplane Authors.
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 function is an SDK for building Composition Functions.
package function
import (
"crypto/tls"
"crypto/x509"
"net"
"os"
"path/filepath"
"github.com/pkg/errors"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
ginsecure "google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/reflection"
"github.com/crossplane/function-sdk-go/logging"
"github.com/crossplane/function-sdk-go/proto/v1beta1"
)
// Default ServeOptions.
const (
DefaultNetwork = "tcp"
DefaultAddress = ":9443"
)
// ServeOptions configure how a Function is served.
type ServeOptions struct {
Network string
Address string
Credentials credentials.TransportCredentials
}
// A ServeOption configures how a Function is served.
type ServeOption func(o *ServeOptions) error
// Listen configures the network and address on which the Function will
// listen for RunFunctionRequests.
func Listen(network, address string) ServeOption {
return func(o *ServeOptions) error {
o.Network = network
o.Address = address
return nil
}
}
// MTLSCertificates specifies a directory from which to load mTLS certificates.
// The directory must contain the server certificate (tls.key and tls.crt), as
// well as a CA certificate (ca.crt) that will be used to authenticate clients.
func MTLSCertificates(dir string) ServeOption {
return func(o *ServeOptions) error {
if dir == "" {
// We want to support passing both MTLSCertificates and
// Insecure as they were supplied as flags. So we don't
// want this to fail because no dir was supplied.
// If no TLS dir is supplied and insecure is false we'll
// return an error due to having no credentials specified.
return nil
}
crt, err := tls.LoadX509KeyPair(
filepath.Clean(filepath.Join(dir, "tls.crt")),
filepath.Clean(filepath.Join(dir, "tls.key")),
)
if err != nil {
return errors.Wrap(err, "cannot load X509 keypair")
}
ca, err := os.ReadFile(filepath.Clean(filepath.Join(dir, "ca.crt")))
if err != nil {
return errors.Wrap(err, "cannot read CA certificate")
}
pool := x509.NewCertPool()
if !pool.AppendCertsFromPEM(ca) {
return errors.New("invalid CA certificate")
}
o.Credentials = credentials.NewTLS(&tls.Config{
MinVersion: tls.VersionTLS12,
Certificates: []tls.Certificate{crt},
ClientCAs: pool,
ClientAuth: tls.RequireAndVerifyClientCert,
})
return nil
}
}
// Insecure specifies whether this Function should be served insecurely - i.e.
// without mTLS authentication. This is only useful for testing and development.
// Crossplane will always send requests using mTLS.
func Insecure(insecure bool) ServeOption {
return func(o *ServeOptions) error {
if insecure {
o.Credentials = ginsecure.NewCredentials()
}
return nil
}
}
// Serve the supplied Function by creating a gRPC server and listening for
// RunFunctionRequests. Blocks until the server returns an error.
func Serve(fn v1beta1.FunctionRunnerServiceServer, o ...ServeOption) error {
so := &ServeOptions{
Network: DefaultNetwork,
Address: DefaultAddress,
}
for _, fn := range o {
if err := fn(so); err != nil {
return errors.Wrap(err, "cannot apply ServeOption")
}
}
if so.Credentials == nil {
return errors.New("no credentials provided - did you specify the Insecure or MTLSCertificates options?")
}
lis, err := net.Listen(so.Network, so.Address)
if err != nil {
return errors.Wrapf(err, "cannot listen for %s connections at address %q", so.Network, so.Address)
}
srv := grpc.NewServer(grpc.Creds(so.Credentials))
reflection.Register(srv)
v1beta1.RegisterFunctionRunnerServiceServer(srv, fn)
return errors.Wrap(srv.Serve(lis), "cannot serve mTLS gRPC connections")
}
// NewLogger returns a new logger.
func NewLogger(debug bool) (logging.Logger, error) {
return logging.NewLogger(debug)
}