Skip to content

Commit e2c11ac

Browse files
committed
adding configurable ssh-hosts for git-based skills
Signed-off-by: Jesus Munoz <jesus.munoz@solo.io>
1 parent 3eb4c76 commit e2c11ac

3 files changed

Lines changed: 76 additions & 12 deletions

File tree

go/core/internal/controller/translator/agent/adk_api_translator.go

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1584,10 +1584,17 @@ func validateSubPath(p string) error {
15841584

15851585
// skillsInitData holds the template data for the unified skills-init script.
15861586
type skillsInitData struct {
1587-
AuthMountPath string // "/git-auth" or "" (for git auth)
1588-
GitRefs []gitRefData // git repos to clone
1589-
OCIRefs []ociRefData // OCI images to pull
1590-
InsecureOCI bool // --insecure flag for krane
1587+
AuthMountPath string // "/git-auth" or "" (for git auth)
1588+
GitRefs []gitRefData // git repos to clone
1589+
OCIRefs []ociRefData // OCI images to pull
1590+
InsecureOCI bool // --insecure flag for krane
1591+
SSHHosts []sshHostData // extra hosts to add to known_hosts via ssh-keyscan
1592+
}
1593+
1594+
// sshHostData holds the host and optional port for an SSH known_hosts entry.
1595+
type sshHostData struct {
1596+
Host string // hostname or IP
1597+
Port string // port number, empty means default (22)
15911598
}
15921599

15931600
// gitRefData holds pre-computed fields for each git skill ref, used by the script template.
@@ -1651,6 +1658,21 @@ func prepareSkillsInitData(
16511658

16521659
if authSecretRef != nil {
16531660
data.AuthMountPath = "/git-auth"
1661+
seenHosts := make(map[string]bool)
1662+
for _, ref := range gitRefs {
1663+
u, err := url.Parse(ref.URL)
1664+
if err != nil || u.Scheme != "ssh" {
1665+
continue
1666+
}
1667+
host := u.Hostname()
1668+
port := u.Port()
1669+
key := host + ":" + port
1670+
if seenHosts[key] {
1671+
continue
1672+
}
1673+
seenHosts[key] = true
1674+
data.SSHHosts = append(data.SSHHosts, sshHostData{Host: host, Port: port})
1675+
}
16541676
}
16551677

16561678
seen := make(map[string]bool)

go/core/internal/controller/translator/agent/git_skills_test.go

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,14 @@ func Test_AdkApiTranslator_Skills(t *testing.T) {
4444
name string
4545
agent *v1alpha2.Agent
4646
// assertions
47-
wantSkillsInit bool
48-
wantSkillsVolume bool
49-
wantContainsBranch string
50-
wantContainsCommit string
51-
wantContainsPath string
52-
wantContainsKrane bool
53-
wantAuthVolume bool
47+
wantSkillsInit bool
48+
wantSkillsVolume bool
49+
wantContainsBranch string
50+
wantContainsCommit string
51+
wantContainsPath string
52+
wantContainsKrane bool
53+
wantAuthVolume bool
54+
wantSSHKeyscanHosts []string // substrings expected in the ssh-keyscan lines
5455
}{
5556
{
5657
name: "no skills - no init containers",
@@ -215,6 +216,34 @@ func Test_AdkApiTranslator_Skills(t *testing.T) {
215216
wantSkillsVolume: true,
216217
wantAuthVolume: true,
217218
},
219+
{
220+
name: "git skills with SSH URL and auth secret scans custom host",
221+
agent: &v1alpha2.Agent{
222+
ObjectMeta: metav1.ObjectMeta{Name: "agent-ssh", Namespace: namespace},
223+
Spec: v1alpha2.AgentSpec{
224+
Type: v1alpha2.AgentType_Declarative,
225+
Declarative: &v1alpha2.DeclarativeAgentSpec{
226+
SystemMessage: "test",
227+
ModelConfig: modelName,
228+
},
229+
Skills: &v1alpha2.SkillForAgent{
230+
GitAuthSecretRef: &corev1.LocalObjectReference{
231+
Name: "gitea-ssh-credentials",
232+
},
233+
GitRefs: []v1alpha2.GitRepo{
234+
{
235+
URL: "ssh://git@gitea-ssh.gitea:22/gitops/ssh-skills-repo.git",
236+
Ref: "main",
237+
},
238+
},
239+
},
240+
},
241+
},
242+
wantSkillsInit: true,
243+
wantSkillsVolume: true,
244+
wantAuthVolume: true,
245+
wantSSHKeyscanHosts: []string{"gitea-ssh.gitea"},
246+
},
218247
{
219248
name: "git skill with custom name",
220249
agent: &v1alpha2.Agent{
@@ -354,11 +383,12 @@ func Test_AdkApiTranslator_Skills(t *testing.T) {
354383

355384
// Check auth volume
356385
if tt.wantAuthVolume {
386+
wantSecretName := tt.agent.Spec.Skills.GitAuthSecretRef.Name
357387
hasAuthVolume := false
358388
for _, v := range deployment.Spec.Template.Spec.Volumes {
359389
if v.Secret != nil && v.Name == "git-auth" {
360390
hasAuthVolume = true
361-
assert.Equal(t, "github-token", v.Secret.SecretName, "auth volume should reference the correct secret")
391+
assert.Equal(t, wantSecretName, v.Secret.SecretName, "auth volume should reference the correct secret")
362392
}
363393
}
364394
assert.True(t, hasAuthVolume, "git-auth volume should exist")
@@ -378,6 +408,15 @@ func Test_AdkApiTranslator_Skills(t *testing.T) {
378408
assert.Contains(t, script, "credential.helper")
379409
}
380410

411+
// Verify custom SSH hosts are scanned
412+
if len(tt.wantSSHKeyscanHosts) > 0 {
413+
require.NotNil(t, skillsInitContainer)
414+
script := skillsInitContainer.Command[2]
415+
for _, host := range tt.wantSSHKeyscanHosts {
416+
assert.Contains(t, script, host, "script should ssh-keyscan custom host %q", host)
417+
}
418+
}
419+
381420
// Verify insecure flag for OCI skills
382421
if tt.agent.Spec.Skills != nil && tt.agent.Spec.Skills.InsecureSkipVerify {
383422
require.NotNil(t, skillsInitContainer)

go/core/internal/controller/translator/agent/skills-init.sh.tmpl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ if [ -f "${_auth_mount}/ssh-privatekey" ]; then
99
cp "${_auth_mount}/ssh-privatekey" ~/.ssh/id_rsa
1010
chmod 600 ~/.ssh/id_rsa
1111
ssh-keyscan github.com gitlab.com bitbucket.org >> ~/.ssh/known_hosts
12+
{{- range .SSHHosts }}
13+
ssh-keyscan{{ if .Port }} -p {{ .Port }}{{ end }} {{ .Host }} >> ~/.ssh/known_hosts
14+
{{- end }}
1215
elif [ -f "${_auth_mount}/token" ]; then
1316
git config --global credential.helper "!f() { echo username=x-access-token; echo password=\$(cat ${_auth_mount}/token); }; f"
1417
fi

0 commit comments

Comments
 (0)