@@ -2,28 +2,33 @@ package localbuild
2
2
3
3
import (
4
4
"context"
5
- "crypto/tls"
6
5
"embed"
6
+ "encoding/base64"
7
7
"fmt"
8
8
"net/http"
9
- "time"
10
9
10
+ "code.gitea.io/sdk/gitea"
11
11
"github.com/cnoe-io/idpbuilder/api/v1alpha1"
12
12
"github.com/cnoe-io/idpbuilder/pkg/k8s"
13
13
"github.com/cnoe-io/idpbuilder/pkg/util"
14
14
corev1 "k8s.io/api/core/v1"
15
+ k8serrors "k8s.io/apimachinery/pkg/api/errors"
15
16
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
17
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
16
18
"k8s.io/apimachinery/pkg/runtime"
17
19
"k8s.io/apimachinery/pkg/runtime/schema"
20
+ "k8s.io/apimachinery/pkg/types"
18
21
ctrl "sigs.k8s.io/controller-runtime"
19
22
"sigs.k8s.io/controller-runtime/pkg/client"
20
23
"sigs.k8s.io/controller-runtime/pkg/log"
21
24
)
22
25
23
26
const (
24
27
// hardcoded values from what we have in the yaml installation file.
25
- giteaNamespace = "gitea"
26
- giteaAdminSecret = "gitea-credential"
28
+ giteaNamespace = "gitea"
29
+ giteaAdminSecret = "gitea-credential"
30
+ giteaAdminTokenName = "admin"
31
+ giteaAdminTokenFieldName = "token"
27
32
// this is the URL accessible outside cluster. resolves to localhost
28
33
giteaIngressURL = "%s://gitea.cnoe.localtest.me:%s"
29
34
// this is the URL accessible within cluster for ArgoCD to fetch resources.
@@ -38,11 +43,7 @@ func RawGiteaInstallResources(templateData any, config v1alpha1.PackageCustomiza
38
43
return k8s .BuildCustomizedManifests (config .FilePath , "resources/gitea/k8s" , installGiteaFS , scheme , templateData )
39
44
}
40
45
41
- func newGiteaAdminSecret () (corev1.Secret , error ) {
42
- pass , err := util .GeneratePassword ()
43
- if err != nil {
44
- return corev1.Secret {}, err
45
- }
46
+ func giteaAdminSecretObject () corev1.Secret {
46
47
return corev1.Secret {
47
48
TypeMeta : metav1.TypeMeta {
48
49
Kind : "Secret" ,
@@ -52,11 +53,20 @@ func newGiteaAdminSecret() (corev1.Secret, error) {
52
53
Name : giteaAdminSecret ,
53
54
Namespace : giteaNamespace ,
54
55
},
55
- StringData : map [string ]string {
56
- "username" : v1alpha1 .GiteaAdminUserName ,
57
- "password" : pass ,
58
- },
59
- }, nil
56
+ }
57
+ }
58
+
59
+ func newGiteaAdminSecret () (corev1.Secret , error ) {
60
+ pass , err := util .GeneratePassword ()
61
+ if err != nil {
62
+ return corev1.Secret {}, err
63
+ }
64
+ obj := giteaAdminSecretObject ()
65
+ obj .StringData = map [string ]string {
66
+ "username" : v1alpha1 .GiteaAdminUserName ,
67
+ "password" : pass ,
68
+ }
69
+ return obj , nil
60
70
}
61
71
62
72
func (r * LocalbuildReconciler ) ReconcileGitea (ctx context.Context , req ctrl.Request , resource * v1alpha1.Localbuild ) (ctrl.Result , error ) {
@@ -75,13 +85,25 @@ func (r *LocalbuildReconciler) ReconcileGitea(ctx context.Context, req ctrl.Requ
75
85
},
76
86
}
77
87
78
- giteCreds , err := newGiteaAdminSecret ()
88
+ sec := giteaAdminSecretObject ()
89
+ err := r .Client .Get (ctx , types.NamespacedName {
90
+ Namespace : sec .GetNamespace (),
91
+ Name : sec .GetName (),
92
+ }, & sec )
93
+
79
94
if err != nil {
80
- return ctrl.Result {}, fmt .Errorf ("generating gitea admin secret: %w" , err )
95
+ if k8serrors .IsNotFound (err ) {
96
+ giteaCreds , err := newGiteaAdminSecret ()
97
+ if err != nil {
98
+ return ctrl.Result {}, fmt .Errorf ("generating gitea admin secret: %w" , err )
99
+ }
100
+ gitea .unmanagedResources = []client.Object {& giteaCreds }
101
+ sec = giteaCreds
102
+ } else {
103
+ return ctrl.Result {}, fmt .Errorf ("getting gitea secret: %w" , err )
104
+ }
81
105
}
82
106
83
- gitea .unmanagedResources = []client.Object {& giteCreds }
84
-
85
107
v , ok := resource .Spec .PackageConfigs .CorePackageCustomization [v1alpha1 .GiteaPackageName ]
86
108
if ok {
87
109
gitea .customization = v
@@ -94,16 +116,8 @@ func (r *LocalbuildReconciler) ReconcileGitea(ctx context.Context, req ctrl.Requ
94
116
baseUrl := giteaBaseUrl (r .Config )
95
117
// need this to ensure gitrepository controller can reach the api endpoint.
96
118
logger .V (1 ).Info ("checking gitea api endpoint" , "url" , baseUrl )
97
- c := & http.Client {
98
- Transport : & http.Transport {
99
- TLSClientConfig : & tls.Config {
100
- InsecureSkipVerify : true ,
101
- },
102
- },
103
- Timeout : 2 * time .Second ,
104
- }
119
+ c := util .GetHttpClient ()
105
120
resp , err := c .Get (baseUrl )
106
-
107
121
if err != nil {
108
122
return ctrl.Result {}, err
109
123
}
@@ -115,6 +129,11 @@ func (r *LocalbuildReconciler) ReconcileGitea(ctx context.Context, req ctrl.Requ
115
129
}
116
130
}
117
131
132
+ err = r .setGiteaToken (ctx , sec , baseUrl )
133
+ if err != nil {
134
+ return ctrl.Result {}, fmt .Errorf ("creating gitea token: %w" , err )
135
+ }
136
+
118
137
resource .Status .Gitea .ExternalURL = baseUrl
119
138
resource .Status .Gitea .InternalURL = giteaInternalBaseUrl (r .Config )
120
139
resource .Status .Gitea .AdminUserSecretName = giteaAdminSecret
@@ -123,6 +142,74 @@ func (r *LocalbuildReconciler) ReconcileGitea(ctx context.Context, req ctrl.Requ
123
142
return ctrl.Result {}, nil
124
143
}
125
144
145
+ func (r * LocalbuildReconciler ) setGiteaToken (ctx context.Context , secret corev1.Secret , baseUrl string ) error {
146
+ _ , ok := secret .Data [giteaAdminTokenFieldName ]
147
+ if ok {
148
+ return nil
149
+ }
150
+
151
+ u := unstructured.Unstructured {}
152
+ u .SetName (giteaAdminSecret )
153
+ u .SetNamespace (giteaNamespace )
154
+ u .SetGroupVersionKind (corev1 .SchemeGroupVersion .WithKind ("Secret" ))
155
+
156
+ user , ok := secret .Data ["username" ]
157
+ if ! ok {
158
+ return fmt .Errorf ("username field not found in gitea secret" )
159
+ }
160
+
161
+ pass , ok := secret .Data ["password" ]
162
+ if ! ok {
163
+ return fmt .Errorf ("password field not found in gitea secret" )
164
+ }
165
+
166
+ t , err := getGiteaToken (ctx , baseUrl , string (user ), string (pass ))
167
+ if err != nil {
168
+ return fmt .Errorf ("getting gitea token: %w" , err )
169
+ }
170
+
171
+ token := base64 .StdEncoding .EncodeToString ([]byte (t ))
172
+ err = unstructured .SetNestedField (u .Object , token , "data" , giteaAdminTokenFieldName )
173
+ if err != nil {
174
+ return fmt .Errorf ("setting gitea token field: %w" , err )
175
+ }
176
+
177
+ return r .Client .Patch (ctx , & u , client .Apply , client .ForceOwnership , client .FieldOwner (v1alpha1 .FieldManager ))
178
+ }
179
+
180
+ func getGiteaToken (ctx context.Context , baseUrl , username , password string ) (string , error ) {
181
+
182
+ giteaClient := gitea .NewClientWithHTTP (baseUrl , util .GetHttpClient ())
183
+ giteaClient .SetBasicAuth (username , password )
184
+ giteaClient .SetContext (ctx )
185
+ tokens , resp , err := giteaClient .ListAccessTokens (gitea.ListAccessTokensOptions {})
186
+ if err != nil {
187
+ return "" , fmt .Errorf ("listing gitea access tokens. stauts: %s error : %w" , resp .Status , err )
188
+ }
189
+
190
+ for i := range tokens {
191
+ if tokens [i ].Name == giteaAdminTokenName {
192
+ resp , err := giteaClient .DeleteAccessToken (tokens [i ].ID )
193
+ if err != nil {
194
+ return "" , fmt .Errorf ("deleting gitea access tokens. stauts: %s error : %w" , resp .Status , err )
195
+ }
196
+ break
197
+ }
198
+ }
199
+
200
+ token , resp , err := giteaClient .CreateAccessToken (gitea.CreateAccessTokenOption {
201
+ Name : giteaAdminTokenName ,
202
+ Scopes : []gitea.AccessTokenScope {
203
+ gitea .AccessTokenScopeAll ,
204
+ },
205
+ })
206
+ if err != nil {
207
+ return "" , fmt .Errorf ("deleting gitea access tokens. stauts: %s error : %w" , resp .Status , err )
208
+ }
209
+
210
+ return token .Token , nil
211
+ }
212
+
126
213
func giteaBaseUrl (config util.CorePackageTemplateConfig ) string {
127
214
return fmt .Sprintf (giteaIngressURL , config .Protocol , config .Port )
128
215
}
0 commit comments