diff --git a/go.mod b/go.mod index ef5dd77466..f788024173 100644 --- a/go.mod +++ b/go.mod @@ -134,6 +134,34 @@ require ( xorm.io/xorm v1.3.9 ) +require ( + github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect + github.com/distribution/reference v0.5.0 // indirect + github.com/docker/distribution v2.8.3+incompatible // indirect + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.23.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/gnostic-models v0.6.9 // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/morikuni/aec v1.0.0 // indirect + golang.org/x/term v0.32.0 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + k8s.io/apimachinery v0.33.3 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect + k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect + sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect +) + require ( cloud.google.com/go/compute/metadata v0.6.0 // indirect dario.cat/mergo v1.0.1 // indirect @@ -190,6 +218,7 @@ require ( github.com/davidmz/go-pageant v1.0.2 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dlclark/regexp2 v1.11.5 // indirect + github.com/docker/docker v24.0.9+incompatible github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 // indirect github.com/fatih/color v1.18.0 // indirect github.com/fxamacker/cbor/v2 v2.8.0 // indirect @@ -279,6 +308,8 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect + k8s.io/api v0.33.3 + k8s.io/client-go v0.33.3 ) replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1 diff --git a/go.sum b/go.sum index e440d0f931..ac4dc16be1 100644 --- a/go.sum +++ b/go.sum @@ -60,6 +60,8 @@ github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 h1:D3occ github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0/go.mod h1:bTSOgj05NGRuHHhQwAdPnYr9TOdNmKlZTgGLL6nyAdI= github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.0 h1:UXT0o77lXQrikd1kgwIPQOUect7EoR/+sbP4wQKdzxM= github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.0/go.mod h1:cTvi54pg19DoT07ekoeMgE/taAwNtCShVeZqA+Iv2xI= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs= @@ -238,6 +240,8 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dimiro1/reply v0.0.0-20200315094148-d0136a4c9e21 h1:PdsjTl0Cg+ZJgOx/CFV5NNgO1ThTreqdgKYiDCMHJwA= github.com/dimiro1/reply v0.0.0-20200315094148-d0136a4c9e21/go.mod h1:xJvkyD6Y2rZapGvPJLYo9dyx1s5dxBEDPa8T3YTuOk0= +github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= +github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/djherbis/buffer v1.1.0/go.mod h1:VwN8VdFkMY0DCALdY8o00d3IZ6Amz/UNVMWcSaJT44o= github.com/djherbis/buffer v1.2.0 h1:PH5Dd2ss0C7CRRhQCZ2u7MssF+No9ide8Ye71nPHcrQ= github.com/djherbis/buffer v1.2.0/go.mod h1:fjnebbZjCUpPinBRD+TDwXSOeNQ7fPQWLfGQqiAiUyE= @@ -248,6 +252,14 @@ github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55k github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= +github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v24.0.9+incompatible h1:HPGzNmwfLZWdxHqK9/II92pyi1EpYKsAqcl4G0Of9v0= +github.com/docker/docker v24.0.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= @@ -266,6 +278,8 @@ github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTe github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 h1:oP4q0fw+fOSWn3DfFi4EXdT+B+gTtzx8GC9xsc26Znk= github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ= github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/ethantkoenig/rupture v1.0.1 h1:6aAXghmvtnngMgQzy7SMGdicMvkV86V4n9fT0meE5E4= @@ -321,6 +335,16 @@ github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-ldap/ldap/v3 v3.4.11 h1:4k0Yxweg+a3OyBLjdYn5OKglv18JNvfDykSoI8bW0gU= github.com/go-ldap/ldap/v3 v3.4.11/go.mod h1:bY7t0FLK8OAVpp/vV6sSlpz3EQDGcQwc8pF0ujLgKvM= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-redis/redis/v7 v7.4.1 h1:PASvf36gyUpr2zdOUS/9Zqc80GbM+9BDyiJSJDDOrTI= @@ -345,6 +369,8 @@ github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6Wezm github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f h1:3BSP1Tbs2djlpprl7wCLuiqMaUh5SJkkzI2gDs+FgLs= github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14= github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85 h1:UjoPNDAQ5JPCjlxoJd6K8ALZqSDDhk2ymieAZOVaDg0= @@ -382,11 +408,14 @@ github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl76 github.com/google/flatbuffers v24.3.25+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q= github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= +github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= @@ -478,6 +507,8 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNU github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= @@ -545,11 +576,15 @@ github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/moby/term v0.0.0-20200312100748-672ec06f55cd h1:aY7OQNf2XqY/JQ6qREWamhI/81os/agb2BAGpcx5yWI= +github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 h1:j2kD3MT1z4PXCiUllUJF9mWUESr9TWKS7iEKsQ/IipM= github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450/go.mod h1:skjdDftzkFALcuGzYSklqYd8gvat6F1gZJ4YPVbkZpM= github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= @@ -726,6 +761,7 @@ github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3i github.com/yohcop/openid-go v1.0.1 h1:DPRd3iPO5F6O5zX2e62XpVAbPT6wV51cuucH0z9g3js= github.com/yohcop/openid-go v1.0.1/go.mod h1:b/AvD03P0KHj4yuihb+VtLD6bYYgsy0zqBzPCRjkCNs= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= @@ -846,6 +882,7 @@ golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -893,8 +930,10 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200928182047-19e03678916f/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= @@ -923,7 +962,11 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= @@ -941,6 +984,20 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= +k8s.io/api v0.33.3 h1:SRd5t//hhkI1buzxb288fy2xvjubstenEKL9K51KBI8= +k8s.io/api v0.33.3/go.mod h1:01Y/iLUjNBM3TAvypct7DIj0M0NIZc+PzAHCIo0CYGE= +k8s.io/apimachinery v0.33.3 h1:4ZSrmNa0c/ZpZJhAgRdcsFcZOw1PQU1bALVQ0B3I5LA= +k8s.io/apimachinery v0.33.3/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= +k8s.io/client-go v0.33.3 h1:M5AfDnKfYmVJif92ngN532gFqakcGi6RvaOF16efrpA= +k8s.io/client-go v0.33.3/go.mod h1:luqKBQggEf3shbxHY4uVENAxrDISLOarxpTKMiUuujg= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw= @@ -965,6 +1022,15 @@ mvdan.cc/xurls/v2 v2.6.0 h1:3NTZpeTxYVWNSokW3MKeyVkz/j7uYXYiMtXRUfmjbgI= mvdan.cc/xurls/v2 v2.6.0/go.mod h1:bCvEZ1XvdA6wDnxY7jPPjEmigDtvtvPXAD/Exa9IMSk= pgregory.net/rapid v0.4.2 h1:lsi9jhvZTYvzVpeG93WWgimPRmiJQfGFRNTEZh1dtY0= pgregory.net/rapid v0.4.2/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= +sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc= +sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 h1:mUcz5b3FJbP5Cvdq7Khzn6J9OCUQJaBwgBkCR+MOwSs= strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:FJGmPh3vz9jSos1L/F91iAgnC/aejc0wIIrF2ZwJxdY= xorm.io/builder v0.3.13 h1:a3jmiVVL19psGeXx8GIurTp7p0IIgqeDmwhcR6BAOAo= diff --git a/modules/docker/docker_api.go b/modules/docker/docker_api.go new file mode 100644 index 0000000000..6d161dbb5b --- /dev/null +++ b/modules/docker/docker_api.go @@ -0,0 +1,117 @@ +package docker + +import ( + "context" + "io" + "os" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/client" +) + +// CreateDockerClient 创建Docker客户端 +func CreateDockerClient(ctx context.Context) (*client.Client, error) { + cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) + if err != nil { + return nil, err + } + return cli, nil +} + +// GetDockerSocketPath 获取Docker Socket路径 +func GetDockerSocketPath() (string, error) { + // 检查常见的Docker socket路径 + socketPaths := []string{ + "/var/run/docker.sock", + "/run/podman/podman.sock", + "$HOME/.colima/docker.sock", + "$XDG_RUNTIME_DIR/docker.sock", + "$XDG_RUNTIME_DIR/podman/podman.sock", + `\\.\pipe\docker_engine`, + "$HOME/.docker/run/docker.sock", + } + + for _, path := range socketPaths { + if _, err := os.Stat(path); err == nil { + return path, nil + } + } + + // 如果找不到,返回默认路径 + return "/var/run/docker.sock", nil +} + +// PullImage 拉取Docker镜像 +func PullImage(cli *client.Client, dockerHost, imageName string) error { + ctx := context.Background() + + reader, err := cli.ImagePull(ctx, imageName, types.ImagePullOptions{}) + if err != nil { + return err + } + defer reader.Close() + + // 读取并丢弃输出,确保拉取完成 + _, err = io.Copy(io.Discard, reader) + return err +} + +// CreateAndStartContainer 创建并启动容器 +func CreateAndStartContainer(cli *client.Client, imageName string, cmd []string, env []string, binds []string, ports map[string]string, containerName string) error { + ctx := context.Background() + + // 配置容器 + config := &container.Config{ + Image: imageName, + Env: env, + } + + if cmd != nil { + config.Cmd = cmd + } + + hostConfig := &container.HostConfig{ + Binds: binds, + } + + // 如果有端口映射配置 + if ports != nil && len(ports) > 0 { + // 这里可以根据需要添加端口映射逻辑 + // hostConfig.PortBindings = portBindings + } + + // 创建容器 + resp, err := cli.ContainerCreate(ctx, config, hostConfig, nil, nil, containerName) + if err != nil { + return err + } + + // 启动容器 + return cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}) +} + +// DeleteContainer 停止并删除指定名称的容器 +func DeleteContainer(cli *client.Client, containerName string) error { + ctx := context.Background() + + // 首先尝试停止容器 + timeout := 10 + err := cli.ContainerStop(ctx, containerName, container.StopOptions{ + Timeout: &timeout, + }) + if err != nil { + // 如果容器已经停止或不存在,继续执行删除操作 + // 这里不返回错误,因为我们的目标是删除容器 + } + + // 删除容器 + err = cli.ContainerRemove(ctx, containerName, types.ContainerRemoveOptions{ + Force: true, // 强制删除,即使容器正在运行 + }) + if err != nil { + return err + } + + return nil +} diff --git a/modules/setting/k8s.go b/modules/setting/k8s.go new file mode 100644 index 0000000000..e0306ec67d --- /dev/null +++ b/modules/setting/k8s.go @@ -0,0 +1,14 @@ +package setting + +var K8sConfig = struct { + Enable bool + Url string + Token string +}{} + +func loadK8sSettingsFrom(rootCfg ConfigProvider) { + sec := rootCfg.Section("k8s") + K8sConfig.Enable = sec.Key("ENABLE").MustBool(false) + K8sConfig.Url = sec.Key("URL").MustString("") + K8sConfig.Token = sec.Key("TOKEN").MustString("") +} diff --git a/modules/setting/runners.go b/modules/setting/runners.go new file mode 100644 index 0000000000..eb0c7c15aa --- /dev/null +++ b/modules/setting/runners.go @@ -0,0 +1,14 @@ +package setting + +var Runner = struct { + AutoStart bool + Count int + Image string +}{} + +func loadRunnerSettingsFrom(rootCfg ConfigProvider) { + sec := rootCfg.Section("runners") + Runner.AutoStart = sec.Key("AUTO_START").MustBool(true) + Runner.Count = sec.Key("RUNNER_COUNT").MustInt(1) + Runner.Image = sec.Key("RUNNER_IMAGE").MustString("devstar.cn/devstar/act_runner:latest") +} diff --git a/modules/setting/setting.go b/modules/setting/setting.go index d7748db28f..92fbd23532 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -217,6 +217,8 @@ func LoadSettings() { loadProjectFrom(CfgProvider) loadMimeTypeMapFrom(CfgProvider) loadFederationFrom(CfgProvider) + loadRunnerSettingsFrom(CfgProvider) + loadK8sSettingsFrom(CfgProvider) loadWechatSettingsFrom(CfgProvider) } @@ -225,6 +227,8 @@ func LoadSettingsForInstall() { loadDBSetting(CfgProvider) loadServiceFrom(CfgProvider) loadMailerFrom(CfgProvider) + loadRunnerSettingsFrom(CfgProvider) + loadK8sSettingsFrom(CfgProvider) loadWechatSettingsFrom(CfgProvider) } diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 8aed12f33c..2256bb1fc2 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -303,6 +303,10 @@ log_root_path = Log Path log_root_path_helper = Log files will be written to this directory. optional_title = Optional Settings +k8s_title = Kubernetes Settings +k8s_enable = Enable Kubernetes +k8s_url = Kubernetes API URL +k8s_token = Kubernetes Token email_title = Email Settings smtp_addr = SMTP Host smtp_port = SMTP Port @@ -3879,6 +3883,8 @@ runners.status.active = Active runners.status.offline = Offline runners.version = Version runners.reset_registration_token = Reset registration token +runners.regist_runner = Register a new runner +runners.regist_runner_success = Register a new runner successfully runners.reset_registration_token_confirm = Would you like to invalidate the current token and generate a new one? runners.regist_runner = Register a new runner runners.regist_runner_success = Register a new runner successfully diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index ecc2c4a866..e472e47746 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -298,6 +298,10 @@ log_root_path=日志路径 log_root_path_helper=日志文件将写入此目录。 optional_title=可选设置 +k8s_title = Kubernetes设置 +k8s_enable = 启用 Kubernetes +k8s_url = Kubernetes API 地址 +k8s_token = Kubernetes 访问令牌 email_title=电子邮箱设置 smtp_addr=SMTP 主机地址 smtp_port=SMTP 端口 @@ -3866,6 +3870,8 @@ runners.status.active=启用 runners.status.offline=离线 runners.version=版本 runners.reset_registration_token=重置注册令牌 +runners.regist_runner=启动并注册一个运行器 +runners.regist_runner_success=成功启动并注册一个运行器 runners.reset_registration_token_confirm=是否吊销当前令牌并生成一个新令牌? runners.regist_runner=启动并注册一个运行器 runners.regist_runner_success=成功启动并注册一个运行器 diff --git a/routers/install/install.go b/routers/install/install.go index d154c1f44e..abc5364d50 100644 --- a/routers/install/install.go +++ b/routers/install/install.go @@ -36,6 +36,7 @@ import ( auth_service "code.gitea.io/gitea/services/auth" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" + runners_service "code.gitea.io/gitea/services/runners" "code.gitea.io/gitea/services/versioned_migration" "gitea.com/go-chi/session" @@ -123,6 +124,10 @@ func Install(ctx *context.Context) { form.AppURL = setting.AppURL form.LogRootPath = setting.Log.RootPath + form.K8sEnable = setting.K8sConfig.Enable + form.K8sUrl = setting.K8sConfig.Url + form.K8sToken = setting.K8sConfig.Token + // E-mail service settings if setting.MailService != nil { form.SMTPAddr = setting.MailService.SMTPAddr @@ -450,6 +455,20 @@ func SubmitInstall(ctx *context.Context) { cfg.Section("wechat").Key("ENABLED_WECHAT_QR_SIGNIN").SetValue("false") } + if form.K8sEnable { + ctx.Data["K8sEnable"] = form.K8sEnable + cfg.Section("k8s").Key("ENABLE").SetValue("true") + cfg.Section("k8s").Key("URL").SetValue(form.K8sUrl) + cfg.Section("k8s").Key("TOKEN").SetValue(form.K8sToken) + } else { + ctx.Data["K8sEnable"] = form.K8sEnable + cfg.Section("k8s").Key("ENABLE").SetValue("false") + } + + cfg.Section("runners").Key("AUTO_START").SetValue("true") + cfg.Section("runners").Key("RUNNER_COUNT").SetValue("1") + cfg.Section("runners").Key("RUNNER_IMAGE").SetValue("devstar.cn/devstar/act_runner:latest") + cfg.Section("openid").Key("ENABLE_OPENID_SIGNIN").SetValue(strconv.FormatBool(form.EnableOpenIDSignIn)) cfg.Section("openid").Key("ENABLE_OPENID_SIGNUP").SetValue(strconv.FormatBool(form.EnableOpenIDSignUp)) cfg.Section("service").Key("DISABLE_REGISTRATION").SetValue(strconv.FormatBool(form.DisableRegistration)) @@ -594,6 +613,8 @@ func SubmitInstall(ctx *context.Context) { } } + runners_service.RegistGlobalRunner(ctx) + setting.ClearEnvConfigKeys() log.Info("First-time run install finished!") InstallDone(ctx) diff --git a/routers/web/shared/actions/runners.go b/routers/web/shared/actions/runners.go index 648f8046a4..aaf06a84b0 100644 --- a/routers/web/shared/actions/runners.go +++ b/routers/web/shared/actions/runners.go @@ -7,6 +7,7 @@ import ( "errors" "net/http" "net/url" + "strings" actions_model "code.gitea.io/gitea/models/actions" "code.gitea.io/gitea/models/db" @@ -18,6 +19,7 @@ import ( shared_user "code.gitea.io/gitea/routers/web/shared/user" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" + runners_services "code.gitea.io/gitea/services/runners" ) const ( @@ -253,6 +255,13 @@ func RunnersEditPost(ctx *context.Context) { runner.Description = form.Description err = actions_model.UpdateRunner(ctx, runner, "description") + agentLabelsStr := ctx.Req.FormValue("agentlabels") + form.AgentLabels = strings.Split(agentLabelsStr, ",") + for i := range form.AgentLabels { + form.AgentLabels[i] = strings.TrimSpace(form.AgentLabels[i]) + } + runner.AgentLabels = form.AgentLabels + err = actions_model.UpdateRunner(ctx, runner, "description", "agent_labels") if err != nil { log.Warn("RunnerDetailsEditPost.UpdateRunner failed: %v, url: %s", err, ctx.Req.URL) ctx.Flash.Warning(ctx.Tr("actions.runners.update_runner_failed")) @@ -285,6 +294,31 @@ func ResetRunnerRegistrationToken(ctx *context.Context) { ctx.JSONRedirect(redirectTo) } +func RegisterARunner(ctx *context.Context) { + rCtx, err := getRunnersCtx(ctx) + if err != nil { + ctx.ServerError("getRunnersCtx", err) + return + } + token, err := actions_model.NewRunnerToken(ctx, rCtx.OwnerID, rCtx.RepoID) + if err != nil { + ctx.ServerError("NewRunnerToken", err) + return + } + regToken := token.Token + requestCtx := ctx.Req.Context() + err = runners_services.RegistRunner(requestCtx, regToken) + if err != nil { + log.Warn("RegistRunner failed: %v, url: %s", err, ctx.Req.URL) + ctx.Flash.Warning(ctx.Tr("actions.runners.regist_runner_failed")) + ctx.Redirect(rCtx.RedirectLink) + return + } + + ctx.Flash.Success(ctx.Tr("actions.runners.regist_runner_success")) + ctx.Redirect(rCtx.RedirectLink) +} + // RunnerDeletePost response for deleting runner func RunnerDeletePost(ctx *context.Context) { rCtx, err := getRunnersCtx(ctx) @@ -306,6 +340,17 @@ func RunnerDeletePost(ctx *context.Context) { successRedirectTo := rCtx.RedirectLink failedRedirectTo := rCtx.RedirectLink + url.PathEscape(ctx.PathParam("runnerid")) + // 删除对应的Docker容器 + if runner.Name != "" { + requestCtx := ctx.Req.Context() + if err := runners_services.DeleteRunnerByName(requestCtx, runner.Name); err != nil { + log.Warn("DeleteRunnerByName failed: %v, runner name: %s, url: %s", err, runner.Name, ctx.Req.URL) + // 即使删除容器失败,我们仍然继续删除数据库记录 + } else { + log.Info("Successfully deleted Docker container for runner: %s", runner.Name) + } + } + if err := actions_model.DeleteRunner(ctx, runner.ID); err != nil { log.Warn("DeleteRunnerPost.UpdateRunner failed: %v, url: %s", err, ctx.Req.URL) ctx.Flash.Warning(ctx.Tr("actions.runners.delete_runner_failed")) diff --git a/routers/web/web.go b/routers/web/web.go index a7cc46169c..58d1de8e24 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -473,6 +473,16 @@ func registerWebRoutes(m *web.Router) { }) } + addSettingsRunnersRegRoutes := func() { + m.Group("/runners", func() { + m.Get("", shared_actions.Runners) + m.Combo("/{runnerid}").Get(shared_actions.RunnersEdit). + Post(web.Bind(forms.EditRunnerForm{}), shared_actions.RunnersEditPost) + m.Post("/{runnerid}/delete", shared_actions.RunnerDeletePost) + m.Get("/regist_runner", shared_actions.RegisterARunner) + }) + } + // FIXME: not all routes need go through same middleware. // Especially some AJAX requests, we can reduce middleware number to improve performance. @@ -668,6 +678,7 @@ func registerWebRoutes(m *web.Router) { m.Group("/actions", func() { m.Get("", user_setting.RedirectToDefaultSetting) addSettingsRunnersRoutes() + addSettingsRunnersRegRoutes() addSettingsSecretsRoutes() addSettingsVariablesRoutes() }, actions.MustEnableActions) @@ -823,6 +834,7 @@ func registerWebRoutes(m *web.Router) { m.Group("/actions", func() { m.Get("", admin.RedirectToDefaultSetting) addSettingsRunnersRoutes() + addSettingsRunnersRegRoutes() addSettingsVariablesRoutes() }) }, adminReq, ctxDataSet("EnableOAuth2", setting.OAuth2.Enabled, "EnablePackages", setting.Packages.Enabled)) @@ -971,6 +983,7 @@ func registerWebRoutes(m *web.Router) { m.Group("/actions", func() { m.Get("", org_setting.RedirectToDefaultSetting) addSettingsRunnersRoutes() + addSettingsRunnersRegRoutes() addSettingsSecretsRoutes() addSettingsVariablesRoutes() }, actions.MustEnableActions) @@ -1163,6 +1176,7 @@ func registerWebRoutes(m *web.Router) { m.Group("/actions", func() { m.Get("", shared_actions.RedirectToDefaultSetting) addSettingsRunnersRoutes() + addSettingsRunnersRegRoutes() addSettingsSecretsRoutes() addSettingsVariablesRoutes() }, actions.MustEnableActions) diff --git a/services/forms/runner.go b/services/forms/runner.go index 6abfc66fc2..9d8d29d126 100644 --- a/services/forms/runner.go +++ b/services/forms/runner.go @@ -15,6 +15,7 @@ import ( // EditRunnerForm form for admin to create runner type EditRunnerForm struct { Description string + AgentLabels []string } // Validate validates form fields diff --git a/services/forms/user_form.go b/services/forms/user_form.go index 5cf4ed67a9..e72df81144 100644 --- a/services/forms/user_form.go +++ b/services/forms/user_form.go @@ -37,6 +37,10 @@ type InstallForm struct { AppURL string `binding:"Required"` LogRootPath string `binding:"Required"` + K8sEnable bool + K8sUrl string + K8sToken string + SMTPAddr string SMTPPort string SMTPFrom string diff --git a/services/runners/runners.go b/services/runners/runners.go new file mode 100644 index 0000000000..21364d93da --- /dev/null +++ b/services/runners/runners.go @@ -0,0 +1,435 @@ +package runners + +import ( + "context" + "fmt" + "net" + "strings" + "time" + + actions_module "code.gitea.io/gitea/models/actions" + docker_module "code.gitea.io/gitea/modules/docker" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" +) + +func RegistGlobalRunner(ctx context.Context) error { + log.Info("获取全局RunnerToken...") + actionRunnerToken, err := actions_module.NewRunnerToken(ctx, 0, 0) + if err != nil { + return fmt.Errorf("获取全局RunnerToken失败:%v", err) + } + runnerCount := setting.Runner.Count + for i := 0; i < runnerCount; i++ { + err := RegistRunner(ctx, actionRunnerToken.Token) + if err != nil { + return fmt.Errorf("注册Runner失败:%v", err) + } + } + return nil +} + +func checkK8sIsEnable() bool { + return setting.K8sConfig.Enable +} + +func RegistRunner(ctx context.Context, token string) error { + log.Info("开始注册Runner...") + var err error + if checkK8sIsEnable() { + err = registK8sRunner(ctx, token) + } else { + err = registDockerRunner(ctx, token) + } + if err != nil { + return fmt.Errorf("注册Runner失败:%v", err) + } + log.Info("Runner注册成功: %s", token) + return nil +} + +func registDockerRunner(ctx context.Context, token string) error { + log.Info("开始注册Runner...") + cli, err := docker_module.CreateDockerClient(ctx) + if err != nil { + return err + } + defer cli.Close() + //拉取act_runner镜像 + dockerHost, err := docker_module.GetDockerSocketPath() + if err != nil { + return fmt.Errorf("获取docker socket路径失败:%v", err) + } + // 拉取镜像 + err = docker_module.PullImage(cli, dockerHost, setting.Runner.Image) + if err != nil { + return fmt.Errorf("拉取act_runner镜像失败:%v", err) + } + //获取本机IP + ips, err := getLocalIP() + if err != nil { + return fmt.Errorf("获取本机IP失败:%v", err) + } + //获取InstanceUrl + conntype := strings.Split(setting.AppURL, "://")[0] + port := setting.HTTPPort + instanceURL := conntype + "://" + ips[0] + ":" + port + timestamp := time.Now().Format("20060102150405") + //Runner配置 + env := []string{ + "GITEA_INSTANCE_URL=" + instanceURL, + "GITEA_RUNNER_REGISTRATION_TOKEN=" + token, + "GITEA_RUNNER_NAME=runner-" + timestamp, + } + binds := []string{ + "/var/run/docker.sock:/var/run/docker.sock", + } + containerName := "runner-" + timestamp + //创建并启动Runner容器 + err = docker_module.CreateAndStartContainer(cli, setting.Runner.Image, nil, env, binds, nil, containerName) + if err != nil { + return fmt.Errorf("创建并注册Runner失败:%v", err) + } + return nil +} + +func DeleteRunnerByName(ctx context.Context, runnerName string) error { + log.Info("开始停止并删除容器: %s", runnerName) + var err error + if checkK8sIsEnable() { + err = deleteK8sRunnerByName(ctx, runnerName) + } else { + err = deleteDockerRunnerByName(ctx, runnerName) + } + if err != nil { + return fmt.Errorf("删除Runner失败:%v", err) + } + log.Info("Runner删除成功: %s", runnerName) + return nil +} + +func deleteDockerRunnerByName(ctx context.Context, runnerName string) error { + log.Info("开始停止并删除容器: %s", runnerName) + + // 创建Docker客户端 + cli, err := docker_module.CreateDockerClient(ctx) + if err != nil { + return fmt.Errorf("Docker client创建失败:%v", err) + } + log.Info("[StopAndRemoveContainer]Docker client创建成功") + defer cli.Close() + err = docker_module.DeleteContainer(cli, runnerName) + if err != nil { + return fmt.Errorf("Runner创建失败:%v", err) + } + return nil +} + +func getLocalIP() ([]string, error) { + var ips []string + interfaces, err := net.Interfaces() + if err != nil { + return nil, err + } + + for _, iface := range interfaces { + if iface.Flags&net.FlagUp == 0 || + iface.Flags&net.FlagLoopback != 0 { + continue + } + + addrs, err := iface.Addrs() + if err != nil { + continue + } + + // 遍历地址列表 + for _, addr := range addrs { + var ip net.IP + switch v := addr.(type) { + case *net.IPNet: + ip = v.IP + case *net.IPAddr: + ip = v.IP + } + if ip == nil || ip.IsLoopback() { + continue + } + + ip = ip.To4() + if ip == nil { + continue // 非IPv4地址 + } + + ips = append(ips, ip.String()) + } + } + if len(ips) == 0 { + return nil, fmt.Errorf("no valid IP address found") + } + return ips, nil +} + +func getK8sUrlAndToken() (string, string, error) { + if !checkK8sIsEnable() { + return "", "", fmt.Errorf("K8s未启用") + } + k8sUrl := setting.K8sConfig.Url + k8sToken := setting.K8sConfig.Token + + if k8sUrl == "" || k8sToken == "" { + return "", "", fmt.Errorf("K8s配置不完整") + } + return k8sUrl, k8sToken, nil +} + +func registK8sRunner(ctx context.Context, token string) error { + log.Info("开始注册Kubernetes Runner: %s", token) + k8sURL, k8sToken, err := getK8sUrlAndToken() + if err != nil { + return fmt.Errorf("获取K8s配置失败: %v", err) + } + + // 测试连接 + err = testKubernetesConnection(k8sURL, k8sToken) + if err != nil { + return fmt.Errorf("Kubernetes连接测试失败: %v", err) + } + + // 创建K8s客户端 + clientset, err := createKubernetesClient(k8sURL, k8sToken) + if err != nil { + return fmt.Errorf("创建Kubernetes客户端失败: %v", err) + } + // 获取实例URL + instanceURL, err := getInstanceURL() + if err != nil { + return fmt.Errorf("获取实例URL失败: %v", err) + } + + // 创建Runner Deployment + deployment, err := createRunnerDeployment(token, instanceURL) + if err != nil { + return fmt.Errorf("创建Runner Deployment配置失败: %v", err) + } + + // 部署到Kubernetes + //namespace := setting.K8sConfig.Namespace + var namespace string + if namespace == "" { + namespace = "act-runner" + } + + _, err = clientset.AppsV1().Deployments(namespace).Create(ctx, deployment, metav1.CreateOptions{}) + if err != nil { + return fmt.Errorf("在Kubernetes中创建Runner Deployment失败: %v", err) + } + + log.Info("成功在Kubernetes中创建Runner: %s", deployment.Name) + return nil +} + +func getInstanceURL() (string, error) { + // 如果AppURL是公网地址,直接使用 + if setting.AppURL != "" && + !strings.Contains(setting.AppURL, "127.0.0.1") && + !strings.Contains(setting.AppURL, "localhost") { + log.Info("使用配置的AppURL: %s", setting.AppURL) + return setting.AppURL, nil + } + + // 否则构建URL + ips, err := getLocalIP() + if err != nil { + return "", fmt.Errorf("获取本机IP失败: %v", err) + } + + if len(ips) == 0 { + return "", fmt.Errorf("没有找到有效的IP地址") + } + + // 使用第一个IP构建URL + conntype := "http" + if strings.Contains(setting.AppURL, "https://") { + conntype = "https" + } + + port := setting.HTTPPort + instanceURL := conntype + "://" + ips[0] + ":" + port + + log.Info("构建的实例URL: %s", instanceURL) + return instanceURL, nil +} +func deleteK8sRunnerByName(ctx context.Context, runnerName string) error { + log.Info("开始删除K8s Runner: %s", runnerName) + + // 创建Kubernetes客户端 + clientset, err := createKubernetesClient(setting.K8sConfig.Url, setting.K8sConfig.Token) + if err != nil { + return fmt.Errorf("创建Kubernetes客户端失败: %v", err) + } + + // 设置namespace,与创建时保持一致 + + namespace := "act-runner" + + // 删除Deployment + err = clientset.AppsV1().Deployments(namespace).Delete(ctx, runnerName, metav1.DeleteOptions{}) + if err != nil { + return fmt.Errorf("删除K8s Runner Deployment失败: %v", err) + } + + log.Info("成功删除K8s Runner Deployment: %s", runnerName) + return nil +} +func createKubernetesClient(k8sURL, token string) (*kubernetes.Clientset, error) { + config := &rest.Config{ + Host: k8sURL, + BearerToken: token, + TLSClientConfig: rest.TLSClientConfig{ + Insecure: true, + }, + } + + // 创建客户端 + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, fmt.Errorf("创建Kubernetes客户端失败: %v", err) + } + + return clientset, nil +} +func testKubernetesConnection(k8sURL, token string) error { + clientset, err := createKubernetesClient(k8sURL, token) + if err != nil { + return err + } + + // 尝试获取节点列表来测试连接 + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + _, err = clientset.CoreV1().Nodes().List(ctx, metav1.ListOptions{Limit: 1}) + if err != nil { + return fmt.Errorf("无法连接到Kubernetes集群: %v", err) + } + + log.Info("Kubernetes连接测试成功") + return nil +} + +func createRunnerDeployment(token, instanceURL string) (*appsv1.Deployment, error) { + timestamp := time.Now().Format("20060102150405") + name := "act-runner-" + timestamp + + labels := map[string]string{ + "app": "act-runner", + "type": "runner", + "version": "1.0", + } + + // 副本数从配置获取 + replicas := int32(1) + + // 创建Deployment配置 + deployment := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Labels: labels, + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &replicas, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "act-runner", + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "app": "act-runner", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "act-runner", // 匹配现有配置中的容器名 + Image: setting.Runner.Image, + Ports: []corev1.ContainerPort{ + { + Name: "http-0", + ContainerPort: 3000, + Protocol: corev1.ProtocolTCP, + }, + }, + Env: []corev1.EnvVar{ + { + Name: "GITEA_INSTANCE_URL", + Value: instanceURL, + }, + { + Name: "GITEA_RUNNER_REGISTRATION_TOKEN", + Value: token, + }, + { + Name: "GITEA_RUNNER_NAME", // 可选:如果需要设置runner名称 + Value: name, + }, + }, + // 移除资源限制以匹配现有配置(现有配置中 resources: {}) + Resources: corev1.ResourceRequirements{}, + // 挂载Docker socket + VolumeMounts: []corev1.VolumeMount{ + { + Name: "docker-sock", + MountPath: "/var/run/docker.sock", + }, + }, + ImagePullPolicy: corev1.PullIfNotPresent, + }, + }, + // Docker socket卷 + Volumes: []corev1.Volume{ + { + Name: "docker-sock", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/var/run/docker.sock", + }, + }, + }, + }, + RestartPolicy: corev1.RestartPolicyAlways, + ServiceAccountName: "default", // 匹配现有配置 + DNSPolicy: corev1.DNSClusterFirst, + // 添加节点选择器(如果需要) + NodeSelector: map[string]string{ + "kubernetes.io/hostname": "node1", // 可以从配置中读取 + }, + // 添加容忍度 + Tolerations: []corev1.Toleration{ + { + Key: "node.kubernetes.io/not-ready", + Operator: corev1.TolerationOpExists, + Effect: corev1.TaintEffectNoExecute, + TolerationSeconds: func() *int64 { i := int64(300); return &i }(), + }, + { + Key: "node.kubernetes.io/unreachable", + Operator: corev1.TolerationOpExists, + Effect: corev1.TaintEffectNoExecute, + TolerationSeconds: func() *int64 { i := int64(300); return &i }(), + }, + }, + }, + }, + }, + } + + return deployment, nil +} diff --git a/templates/install.tmpl b/templates/install.tmpl index fea40eaca5..a6ccf49bca 100644 --- a/templates/install.tmpl +++ b/templates/install.tmpl @@ -158,6 +158,27 @@

{{ctx.Locale.Tr "install.optional_title"}}

+ +
+ + {{ctx.Locale.Tr "install.k8s_title"}} + +
+
+ + +
+
+
+ + +
+
+ + +
+
+
diff --git a/templates/shared/actions/runner_edit.tmpl b/templates/shared/actions/runner_edit.tmpl index d452d69f7a..f7c57b31ab 100644 --- a/templates/shared/actions/runner_edit.tmpl +++ b/templates/shared/actions/runner_edit.tmpl @@ -36,6 +36,11 @@ +
+ + +
+
diff --git a/templates/shared/actions/runner_list.tmpl b/templates/shared/actions/runner_list.tmpl index 43321a8dc5..9b2b90874c 100644 --- a/templates/shared/actions/runner_list.tmpl +++ b/templates/shared/actions/runner_list.tmpl @@ -9,6 +9,9 @@ {{svg "octicon-triangle-down" 14 "dropdown icon"}}