Documentation
¶
Overview ¶
Package gitea 는 Gitea REST API 로 리포지토리를 프로비저닝한다.
GitHub 어댑터와 형태가 비슷하지만 세 가지가 다르다.
하나, 조직을 만들 수 있다. GitHub Organization 은 API 로 생성되지 않아 EnsureGroup 이 "확인"만 하지만, Gitea 는 실제로 만든다 — 스택 안에 설치되는 Git 서버라 우리가 소유자다.
둘, 파일 커밋에 Git Data API 대신 배치 contents API 를 쓴다. Gitea 는 POST /repos/{owner}/{repo}/contents 하나로 여러 파일을 한 커밋에 담을 수 있다. 스캐폴딩이 여러 커밋으로 쪼개지면 Argo CD 가 중간 상태를 동기화한다.
셋, 클러스터 안에 있다. 주소가 서비스 DNS 라 외부 노출이 필요 없다.
Index ¶
- Constants
- func AdminPasswordPath(env, orgID string) string
- func CISecretName(app string) string
- func TokenSecretPath(env, orgID string) string
- type Client
- func (c *Client) BaseURL() string
- func (c *Client) CommitFiles(ctx context.Context, projectID string, spec port.CommitSpec) error
- func (c *Client) DeleteProject(ctx context.Context, projectID string) error
- func (c *Client) EnsureGroup(ctx context.Context, spec port.GroupSpec) (*port.SCMGroup, error)
- func (c *Client) EnsureOrgMember(ctx context.Context, org, email string) error
- func (c *Client) EnsureProject(ctx context.Context, spec port.ProjectSpec) (*port.SCMProject, error)
- func (c *Client) EnsureWebhook(ctx context.Context, projectID, targetURL, secret string) error
- func (c *Client) Ping(ctx context.Context) error
- func (c *Client) WithHTTPClient(h *http.Client) *Client
- type CredentialPlane
- type KubectlRunner
- type SecretStore
- type SecretWriter
- type TokenIssuer
Constants ¶
const ( // ESOSecretStoreName 은 스택 네임스페이스에 있는 SecretStore 이름이다. // // 스택 모듈이 만든다(helm/external-secrets.go). 모듈 간 직접 import 가 // 금지돼 값을 각자 들고 있으므로, 갈라지면 ESO 가 Secret 을 만들지 못하고 // agent 파드는 FailedMount 로 멈춘다. ESOSecretStoreName = "nullus-openbao" // CISecretPrefix 는 파이프라인 자격증명 Secret 이름의 접두사다. // // Argo CD 리포 Secret 의 기존 규약(nullus-repo-<app>)과 짝을 맞춘다. // 파이프라인 단위라 한 자격증명의 유출이 다른 파이프라인으로 번지지 않고, // 파이프라인을 지울 때 함께 지울 수 있다. CISecretPrefix = "nullus-ci-" )
const ( // SecretProvider 는 토큰을 보관할 시크릿 백엔드다. SecretProvider = "openbao" // AutomationTokenName 은 Nullus 가 발급하는 액세스 토큰의 이름이다. // 사람이 만든 토큰과 구분되어야 재발급 시 정리 대상을 특정할 수 있다. AutomationTokenName = "nullus-automation" // AutomationUser 는 차트가 만드는 관리자 계정이다. // 스택 설치의 provisioning_secrets 가 같은 이름으로 자격증명을 만든다 // (internal/stack/domain.GiteaAdminUser). 갈라지면 인증이 실패한다. AutomationUser = "gitea_admin" )
const (
// APIPathPrefix 는 Gitea REST API 의 경로 접두사다.
APIPathPrefix = "/api/v1"
)
Variables ¶
This section is empty.
Functions ¶
func AdminPasswordPath ¶
AdminPasswordPath 는 Gitea 관리자 비밀번호의 시크릿 경로다.
스택 설치의 provisioning_secrets 가 쓰는 경로와 같아야 한다 (helm/secret-provisioning.go 의 "artifacts/gitea/admin-password"). 토큰 폐기에 관리자 자격이 필요하다 — CLI 에 삭제 명령이 없어 API 를 쓴다.
func CISecretName ¶
CISecretName 은 앱의 파이프라인 자격증명 Secret 이름이다.
스캐폴딩이 만든 Jenkinsfile 의 envFrom.secretRef 가 같은 이름을 가리킨다 (scaffold 의 ciSecretName). 갈라지면 agent 파드가 없는 Secret 을 참조해 기동하지 못한다.
func TokenSecretPath ¶
TokenSecretPath 는 액세스 토큰의 시크릿 경로다.
스택 모듈의 규약(kv/nullus/{env}/{org}/{module}/{provider}/...)을 따른다.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client 는 port.SCMProvisioner 의 Gitea 구현체다.
func NewClient ¶
NewClient 는 Gitea REST 클라이언트를 만든다.
baseURL 은 API 접두사를 뺀 서버 주소다 (예: http://gitea-http.nullus.svc:3000). token 은 Gitea 액세스 토큰이다.
func (*Client) CommitFiles ¶
CommitFiles 는 여러 파일을 한 커밋으로 올린다(upsert).
커밋이 여러 개로 쪼개지면 Argo CD 가 중간 상태를 동기화한다 — 매니페스트만 있고 이미지가 아직 없는 커밋을 배포하려다 실패한다. Gitea 의 배치 contents API 로 한 번에 밀어 넣는다.
파일마다 create / update 를 나눠야 한다. Gitea 는 기존 파일을 update 로 보내면서 그 파일의 sha 를 함께 요구한다 — create 로 보내면 409 가 난다.
func (*Client) DeleteProject ¶
DeleteProject 는 리포지토리를 지운다.
이미 없으면 성공으로 본다 — 삭제의 목표는 "없는 상태" 이고, 404 를 오류로 올리면 앞선 시도가 절반쯤 성공한 뒤 재시도할 때 영영 끝나지 않는다.
func (*Client) EnsureGroup ¶
EnsureGroup 은 조직을 조회하고 없으면 만든다.
GitHub 과 달리 Gitea 는 조직을 API 로 만들 수 있다 — 스택 안에 설치되는 Git 서버라 우리가 소유자다.
func (*Client) EnsureOrgMember ¶
EnsureOrgMember 는 이메일로 찾은 사용자를 조직의 write 팀에 넣는다.
플랫폼이 만든 저장소는 자동화 계정(gitea_admin) 소유의 private 조직 안에 있다. SSO 로 들어온 사람은 그 조직의 멤버가 아니라, 로그인은 되는데 화면이 텅 비어 보인다 — 그런데 개발자는 그 저장소에 앱 소스를 밀어야 한다.
멱등하다. 이미 멤버면 Gitea 가 성공으로 답한다.
func (*Client) EnsureProject ¶
func (c *Client) EnsureProject(ctx context.Context, spec port.ProjectSpec) (*port.SCMProject, error)
EnsureProject 는 리포지토리를 조회하고 없으면 만든다.
func (*Client) EnsureWebhook ¶
EnsureWebhook 은 리포지토리에 webhook 을 걸어 둔다(멱등).
Jenkins multibranch job 은 스스로 폴링하지 않는 한 새 커밋을 모른다. 폴링은 지연이 크고 리포가 늘수록 부하가 커지므로 push webhook 을 건다.
type CredentialPlane ¶
type CredentialPlane struct {
// contains filtered or unexported fields
}
CredentialPlane 은 파이프라인 자격증명을 OpenBao → ESO → K8s Secret 으로 나른다.
Gitea 에는 GitLab 같은 프로젝트 CI 변수 저장소가 없다. Jenkins Credentials 를 1차 저장소로 쓰지 않는 이유는 자격증명 사본이 하나 더 생기고 회전 경로가 둘로 갈리기 때문이다 — OpenBao 가 단일 출처라는 원칙이 깨진다.
매니페스트를 적용하지 않고 렌더링만 한다. 클러스터 접근은 유스케이스가 한곳에서 맡는다(Argo CD 리소스와 같은 경로로 적용된다).
func NewCredentialPlane ¶
func NewCredentialPlane(secrets SecretWriter, env, orgID, stackID, namespace string) *CredentialPlane
NewCredentialPlane 은 CredentialPlane 을 만든다.
func (*CredentialPlane) Provision ¶
func (p *CredentialPlane) Provision( ctx context.Context, app string, vars []port.PipelineVariable, ) (string, error)
Provision 은 값들을 OpenBao 에 쓰고 그것을 참조하는 ExternalSecret 을 만든다.
변수를 한 번에 다 받는다 — ExternalSecret 은 항목 전체를 한 문서로 선언하므로 변수마다 따로 적용하면 마지막 것만 남는다.
type KubectlRunner ¶
KubectlRunner 는 kubectl 실행을 추상화한다.
다른 모듈의 동일 타입을 재사용하지 않는다 — 모듈 간 직접 import 를 피하기 위해 CI/CD 컨텍스트가 자기 계약을 소유한다.
type SecretStore ¶
type SecretStore interface {
GetTokenForStack(ctx context.Context, provider, stackID, path string) (string, error)
PutTokenForStack(ctx context.Context, provider, stackID, path, value string) error
}
SecretStore 는 토큰 보관에 필요한 최소 동작만 노출한다.
스택 범위 접근을 쓴다 — OpenBao 는 스택마다 배포되므로 전역 주소가 하나일 수 없고, 해당 스택의 저장소에 넣어야 ESO 와 같은 곳을 본다.
type SecretWriter ¶
type SecretWriter interface {
PutTokenForStack(ctx context.Context, provider, stackID, path, value string) error
}
SecretWriter 는 OpenBao 기록에 필요한 최소 동작만 노출한다.
type TokenIssuer ¶
type TokenIssuer struct {
// contains filtered or unexported fields
}
TokenIssuer 는 Gitea 액세스 토큰을 확보한다.
Gitea 를 외부에 노출하지 않고도 동작해야 하므로 API 가 아니라 파드 안의 gitea CLI 로 발급한다 — GitLab 이 toolbox 의 rails 콘솔을 쓰는 것과 같은 이유다. 토큰 값은 발급 시점에만 읽을 수 있어 즉시 시크릿 저장소에 보관한다.
func NewTokenIssuer ¶
func NewTokenIssuer(kubeconfig port.KubeconfigProvider, runKubectl KubectlRunner, secrets SecretStore) *TokenIssuer
NewTokenIssuer 는 TokenIssuer 를 만든다.
func (*TokenIssuer) EnsureToken ¶
func (t *TokenIssuer) EnsureToken(ctx context.Context, spec port.SCMTokenSpec) (string, error)
EnsureToken 은 보관된 토큰을 돌려주고, 없거나 Force 면 새로 발급한다.