-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathfactory.go
124 lines (107 loc) · 3.26 KB
/
factory.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
package pythondeployer
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"strings"
"sync/atomic"
"go.arcalot.io/exex"
"go.arcalot.io/log/v2"
"go.flow.arcalot.io/deployer"
"go.flow.arcalot.io/pluginsdk/schema"
"go.flow.arcalot.io/pythondeployer/internal/cliwrapper"
"go.flow.arcalot.io/pythondeployer/internal/config"
"go.flow.arcalot.io/pythondeployer/internal/connector"
)
// NewFactory creates a new factory for the Docker deployer.
func NewFactory() deployer.ConnectorFactory[*config.Config] {
connectorCounter := int64(0)
return &factory{
connectorCounter: &connectorCounter,
}
}
type factory struct {
connectorCounter *int64
}
func (f factory) Name() string {
return "python"
}
func (f factory) DeploymentType() deployer.DeploymentType {
return "python"
}
func (f factory) ConfigurationSchema() *schema.TypedScopeSchema[*config.Config] {
return Schema
}
func (f factory) NextConnectorIndex() int64 {
return atomic.AddInt64(f.connectorCounter, 1)
}
func (f factory) Create(config *config.Config, logger log.Logger) (deployer.Connector, error) {
pythonPath, err := binaryCheck(config.PythonPath)
if err != nil {
return &connector.Connector{}, fmt.Errorf("python binary check failed with error: %w", err)
}
pythonSemver := config.PythonSemVer
if pythonSemver == "" {
outputSemver, err := f.parsePythonVersion(pythonPath)
if err != nil {
return nil, err
}
pythonSemver = outputSemver
}
connectorFilename := strings.Join([]string{
"connector",
strings.Replace(pythonSemver, ".", "-", -1),
strconv.FormatInt(f.NextConnectorIndex(), 10)},
"_")
absWorkDir, err := filepath.Abs(config.WorkDir)
if err != nil {
return nil, fmt.Errorf(
"error determining absolute path for python deployer's working directory (%w) given directory %s",
err, config.WorkDir)
}
connectorFilepath := filepath.Join(absWorkDir, connectorFilename)
err = os.MkdirAll(connectorFilepath, 0750)
if err != nil {
return nil, fmt.Errorf(
"error creating temporary directory for python connector (%w)", err)
}
pythonCli := cliwrapper.NewCliWrapper(pythonPath, connectorFilepath, logger)
cn := connector.NewConnector(
config, logger, connectorFilepath, pythonCli)
return &cn, nil
}
// parsePythonVersion function gets the output of a command that asks the
// Python executable for its semantic version string.
func (f factory) parsePythonVersion(pythonPath string) (string, error) {
versionCmd := exex.Command(pythonPath, "--version")
output, err := versionCmd.Output()
if err != nil {
return "", exex.CommandError(err, "error getting python version")
}
re, err := regexp.Compile(`\d+\.\d+\.\d+`)
if err != nil {
return "", err
}
found := re.FindString(string(output))
return found, nil
}
// binaryCheck validates there is a python binary in a valid absolute path
func binaryCheck(pythonPath string) (string, error) {
if pythonPath == "" {
pythonPath = "python"
}
if !filepath.IsAbs(pythonPath) {
pythonPathAbs, err := exec.LookPath(pythonPath)
if err != nil {
return "", fmt.Errorf("pythonPath executable not found in a valid path with error: %w", err)
}
pythonPath = pythonPathAbs
}
if _, err := os.Stat(pythonPath); err != nil {
return "", fmt.Errorf("pythons binary not found with error: %w", err)
}
return pythonPath, nil
}