Skip to content

Commit 9e9430c

Browse files
Adding Sealights Hook Support (#809)
Signed-off-by: liorn <lior@belugahealth.com> Co-authored-by: Brayan Henao <bhenao@vmware.com>
1 parent 77ed29b commit 9e9430c

7 files changed

Lines changed: 645 additions & 0 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
web: python app.py

fixtures/services/sealights/app.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"""Cloud Foundry test"""
2+
import os
3+
from flask import Flask, request
4+
5+
app = Flask(__name__)
6+
7+
port = int(os.getenv('VCAP_APP_PORT', 8080))
8+
9+
10+
@app.route('/health')
11+
def health():
12+
return "OK"
13+
14+
15+
@app.route('/')
16+
def base():
17+
return "OK"
18+
19+
20+
@app.route('/add', methods=['GET'])
21+
def add():
22+
a = request.args.get('a')
23+
b = request.args.get('b')
24+
try:
25+
a = int(a)
26+
b = int(b)
27+
except ValueError:
28+
return "Error: Please provide valid integer values for the 'a' and 'b' parameters."
29+
30+
return str(a + b)
31+
32+
app.debug = True
33+
34+
if __name__ == "__main__":
35+
app.run(host='0.0.0.0', port=port)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Flask==2.3.2
2+
requests==2.30.0
3+
sealights-python-agent

src/python/hooks/sealights.go

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
package hooks
2+
3+
import (
4+
"encoding/json"
5+
"errors"
6+
"fmt"
7+
"github.com/cloudfoundry/libbuildpack"
8+
"os"
9+
"path/filepath"
10+
"strings"
11+
)
12+
13+
type SealightsPlan struct {
14+
Credentials SealightsCredentials `json:"credentials"`
15+
Name string `json:"name,omitempty"`
16+
}
17+
type SealightsCredentials struct {
18+
Token string `json:"token",omitempty`
19+
TokenFile string `json:"tokenFile",omitempty`
20+
BsId string `json:"bsid",omitempty`
21+
BsIdFile string `json:"bsidFile",omitempty`
22+
Proxy string `json:"proxy",omitempty`
23+
LabId string `json:"labid",omitempty`
24+
Version string `json:"version",omitempty`
25+
}
26+
type SealightsConfig struct {
27+
Token string
28+
TokenFile string
29+
BsId string
30+
BsIdFile string
31+
Proxy string
32+
LabId string
33+
Version string
34+
}
35+
36+
func NewSealightsConfig() *SealightsConfig {
37+
return &SealightsConfig{
38+
Token: "",
39+
TokenFile: "",
40+
BsId: "",
41+
BsIdFile: "",
42+
Proxy: "",
43+
LabId: "",
44+
}
45+
}
46+
47+
func (sc *SealightsConfig) getEnv(key, fallback string) string {
48+
if value, ok := os.LookupEnv(key); ok && value != "" {
49+
return value
50+
}
51+
return fallback
52+
}
53+
func (sc *SealightsConfig) parseSealightsPlan(plan SealightsPlan) *SealightsConfig {
54+
sc.Token = sc.getEnv("SL_TOKEN", plan.Credentials.Token)
55+
sc.TokenFile = sc.getEnv("SL_TOKEN_FILE", plan.Credentials.TokenFile)
56+
sc.BsId = sc.getEnv("SL_BUILD_SESSION_ID", plan.Credentials.BsId)
57+
sc.BsIdFile = sc.getEnv("SL_BUILD_SESSION_ID_FILE", plan.Credentials.BsIdFile)
58+
sc.Proxy = sc.getEnv("SL_PROXY", plan.Credentials.Proxy)
59+
sc.LabId = sc.getEnv("SL_LAB_ID", plan.Credentials.LabId)
60+
return sc
61+
}
62+
63+
func (sc *SealightsConfig) GetStartFlags() string {
64+
var flags []string
65+
if sc.Token != "" {
66+
flags = append(flags, fmt.Sprintf("--token %s", sc.Token))
67+
} else if sc.TokenFile != "" {
68+
flags = append(flags, fmt.Sprintf("--tokenfile %s", sc.TokenFile))
69+
}
70+
if sc.BsId != "" {
71+
flags = append(flags, fmt.Sprintf("--buildsessionid %s", sc.BsId))
72+
} else if sc.BsIdFile != "" {
73+
flags = append(flags, fmt.Sprintf("--buildsessionidfile %s", sc.BsIdFile))
74+
}
75+
if sc.Proxy != "" {
76+
flags = append(flags, fmt.Sprintf("--proxy %s", sc.Proxy))
77+
}
78+
if sc.LabId != "" {
79+
flags = append(flags, fmt.Sprintf("--labid %s", sc.LabId))
80+
}
81+
if len(flags) == 0 {
82+
return ""
83+
}
84+
return " " + strings.Join(flags, " ")
85+
}
86+
87+
type SealightsHook struct {
88+
libbuildpack.DefaultHook
89+
Log *libbuildpack.Logger
90+
}
91+
92+
func (sh SealightsHook) BeforeCompile(stager *libbuildpack.Stager) error {
93+
vcapServices := os.Getenv("VCAP_SERVICES")
94+
services := make(map[string][]SealightsPlan)
95+
96+
err := json.Unmarshal([]byte(vcapServices), &services)
97+
if err != nil {
98+
sh.Log.Debug("Could not unmarshall VCAP_SERVICES JSON exiting: %v", err)
99+
return nil
100+
}
101+
102+
sealgithsServiceName, sealightsPlan := getSealightsServiceName(services, sh.Log)
103+
104+
if sealgithsServiceName == "" {
105+
sh.Log.Debug("No Sealights service found, exiting")
106+
return nil
107+
}
108+
sh.Log.BeginStep("Setting up Sealights hook")
109+
sealightsConfig := NewSealightsConfig().parseSealightsPlan(sealightsPlan)
110+
if err := sh.RewriteRequirementsFile(stager, sealightsConfig.Version); err != nil {
111+
sh.Log.Error("Could not write requirements file with Sealights package: %v", err)
112+
return err
113+
}
114+
115+
if err := sh.RewriteProcFileWithSealgiths(stager, sealightsConfig.GetStartFlags()); err != nil {
116+
sh.Log.Error("Failed to rewrite Procfile with Sealights: %s", err.Error())
117+
return fmt.Errorf("Failed to rewrite Procfile with Sealights: %s", err.Error())
118+
}
119+
120+
sh.Log.Info("Successfully set up Sealights hook")
121+
return nil
122+
}
123+
func (sh SealightsHook) RewriteProcFileWithSealgiths(stager *libbuildpack.Stager, cfgFlags string) error {
124+
sh.Log.BeginStep("Rewriting ProcFile to start with Sealights")
125+
126+
file := filepath.Join(stager.BuildDir(), "Procfile")
127+
128+
if exists, _ := libbuildpack.FileExists(file); exists {
129+
if err := sh.RewriteProcFile(file, cfgFlags); err != nil {
130+
return err
131+
}
132+
fileContents, _ := os.ReadFile(file)
133+
sh.Log.Info(string(fileContents))
134+
} else {
135+
sh.Log.Info("Cannot find Procfile, skipping this step!")
136+
}
137+
return nil
138+
}
139+
func getSealightsServiceName(services map[string][]SealightsPlan, log *libbuildpack.Logger) (string, SealightsPlan) {
140+
for serviceName, servicePlans := range services {
141+
if strings.Contains(serviceName, "sealights") {
142+
return serviceName, servicePlans[0]
143+
}
144+
}
145+
146+
// checking if there is a user-provided service with name sealights
147+
userProvidedServices, keyExists := services["user-provided"]
148+
if !keyExists {
149+
return "", SealightsPlan{}
150+
}
151+
152+
for _, plan := range userProvidedServices {
153+
if strings.Contains(plan.Name, "sealights") {
154+
return plan.Name, plan
155+
}
156+
}
157+
return "", SealightsPlan{}
158+
}
159+
160+
func (sh SealightsHook) RewriteProcFile(procFilePath string, cfgFlags string) error {
161+
startCommand, err := os.ReadFile(procFilePath)
162+
if err != nil {
163+
return fmt.Errorf("Error reading file %s: %v", procFilePath, err)
164+
}
165+
newCommand, err := sh.GenerateStartUpCommand(string(startCommand), cfgFlags)
166+
if err != nil {
167+
return err
168+
}
169+
170+
if err := os.WriteFile(procFilePath, []byte(newCommand), 0666); err != nil {
171+
return fmt.Errorf("Error writing file %s: %v", procFilePath, err)
172+
}
173+
return nil
174+
}
175+
176+
func (sh SealightsHook) GenerateStartUpCommand(startCommand string, cfgFlags string) (string, error) {
177+
webCommands := strings.SplitN(startCommand, ":", 2)
178+
if len(webCommands) != 2 {
179+
return "", errors.New("improper format found in Procfile")
180+
}
181+
return fmt.Sprintf("web: sl-python run%s -- %s", cfgFlags, webCommands[1]), nil
182+
}
183+
func (sh SealightsHook) RewriteRequirementsFile(stager *libbuildpack.Stager, version string) error {
184+
sealightsPackage := "sealights-python-agent"
185+
if version != "" {
186+
sealightsPackage = fmt.Sprintf("sealights-python-agent==%s", version)
187+
}
188+
reqFile := filepath.Join(stager.BuildDir(), "requirements.txt")
189+
writeFlag := os.O_APPEND | os.O_WRONLY
190+
packageName := "\n" + sealightsPackage
191+
192+
if exists, err := libbuildpack.FileExists(reqFile); err != nil {
193+
return err
194+
} else if !exists {
195+
sh.Log.Info("Requirements file not found creating one with sealights packages")
196+
writeFlag = os.O_CREATE | os.O_WRONLY
197+
packageName = sealightsPackage
198+
}
199+
f, err := os.OpenFile(reqFile, writeFlag, 0666)
200+
if err != nil {
201+
return err
202+
}
203+
defer func() {
204+
_ = f.Close()
205+
}()
206+
if _, err = f.WriteString(packageName); err != nil {
207+
return err
208+
}
209+
return nil
210+
}
211+
212+
func init() {
213+
logger := libbuildpack.NewLogger(os.Stdout)
214+
libbuildpack.AddHook(&SealightsHook{
215+
Log: logger,
216+
})
217+
}

0 commit comments

Comments
 (0)