feat(edge): OTO bootstrap 설정 계약을 추가한다
OTO agent bootstrap command 발급 전 Edge config가 OTO script URL과 release base URL을 보존하고 검증할 수 있어야 한다.\n\nRoadmap/task 산출물도 함께 기록해 다음 agent register command 작업이 같은 마일스톤 컨텍스트를 이어받도록 한다.
This commit is contained in:
parent
167dd26c81
commit
e5c3a9dabb
13 changed files with 1027 additions and 44 deletions
|
|
@ -2,14 +2,14 @@
|
|||
|
||||
## 활성 Phase
|
||||
|
||||
- [진행중] Control Plane과 Client 운영
|
||||
- 경로: `agent-roadmap/phase/control-plane-portal-ops/PHASE.md`
|
||||
- [진행중] Automation Runtime과 Bridge 확장
|
||||
- 경로: `agent-roadmap/phase/automation-runtime-bridge/PHASE.md`
|
||||
|
||||
## 활성 Milestone
|
||||
|
||||
- [계획] Multi-Edge 운영
|
||||
- Phase: `agent-roadmap/phase/control-plane-portal-ops/PHASE.md`
|
||||
- 경로: `agent-roadmap/phase/control-plane-portal-ops/milestones/multi-edge-operations.md`
|
||||
- [진행중] OTO Bootstrap Command 발급
|
||||
- Phase: `agent-roadmap/phase/automation-runtime-bridge/PHASE.md`
|
||||
- 경로: `agent-roadmap/phase/automation-runtime-bridge/milestones/oto-bootstrap-command-issue.md`
|
||||
|
||||
## 선택 규칙
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ CLI 실행, specialized agent 등록, bootstrap/enrollment, 원격 터미널 브
|
|||
- 경로: `agent-roadmap/phase/automation-runtime-bridge/milestones/specialized-agent-proto-socket-foundation.md`
|
||||
- 요약: OTO의 node-compatible proto-socket registration smoke를 바탕으로 Edge의 specialized agent kind, registry/config, message boundary, reconnect 정책과 runtime 경로 구분을 완료 후보로 정리했다.
|
||||
|
||||
- [계획] OTO Bootstrap Command 발급
|
||||
- [진행중] OTO Bootstrap Command 발급
|
||||
- 경로: `agent-roadmap/phase/automation-runtime-bridge/milestones/oto-bootstrap-command-issue.md`
|
||||
- 요약: `iop-edge`가 OTO agent record를 만들고 OTO Linux bootstrap script가 소비할 one-line command를 발급하는 CLI/config 표면을 구현한다.
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ Edge는 OTO bootstrap script를 구현하지 않고, OTO가 소유한 script 인
|
|||
|
||||
## 상태
|
||||
|
||||
[계획]
|
||||
[진행중]
|
||||
|
||||
## 구현 잠금
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,188 @@
|
|||
<!-- task=m-oto-bootstrap-command-issue/01_oto_bootstrap_config plan=0 tag=CONFIG -->
|
||||
|
||||
# Code Review Reference - CONFIG
|
||||
|
||||
> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
|
||||
> The task is NOT complete until every implementation-owned section below is filled in.
|
||||
> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving.
|
||||
> Fill implementation-owned sections, then stop with active files in place and report ready for review.
|
||||
> If implementation is blocked by a user-only decision, user-owned external environment prerequisite, or scope conflict, fill `사용자 리뷰 요청` with evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`. Evidence gaps that a follow-up agent can close by rerunning commands or collecting artifacts are normal follow-up issues, not user-review blockers by themselves.
|
||||
> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only, even after compaction/resume.
|
||||
> Follow the ownership table at the bottom of this file for which sections you own.
|
||||
|
||||
## 개요
|
||||
|
||||
date=2026-06-03
|
||||
task=m-oto-bootstrap-command-issue/01_oto_bootstrap_config, plan=0, tag=CONFIG
|
||||
|
||||
## 이 파일을 읽는 리뷰 에이전트에게
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.
|
||||
|
||||
각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
|
||||
리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다.
|
||||
|
||||
1. 판정을 append한다.
|
||||
2. `CODE_REVIEW-{review_lane}-GNN.md` -> `code_review_{review_lane}_GNN_N.log`, `PLAN-{build_lane}-GNN.md` -> `plan_{build_lane}_GNN_M.log`로 아카이브한다.
|
||||
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/{task_name}/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다. `USER_REVIEW.md`가 사용자 결정으로 완료/PASS 해소되면 code-review가 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log` 작성 후 archive 이동한다.
|
||||
4. PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다.
|
||||
5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다.
|
||||
|
||||
---
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
|------|---------|
|
||||
| [CONFIG-1] Edge bootstrap config에 OTO script/release URL 필드를 추가하고 load/default/example 테스트를 작성한다. | [x] |
|
||||
| [CONFIG-2] YAML patch helper가 `artifact_base_url`, `oto_bootstrap_script_url`, `oto_release_base_url`을 같은 방식으로 보존/갱신하게 만든다. | [x] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [x] [CONFIG-1] Edge bootstrap config에 OTO script/release URL 필드를 추가하고 load/default/example 테스트를 작성한다.
|
||||
- [x] [CONFIG-2] YAML patch helper가 `artifact_base_url`, `oto_bootstrap_script_url`, `oto_release_base_url`을 같은 방식으로 보존/갱신하게 만든다.
|
||||
- [x] [CONFIG-3] `go test -count=1 ./packages/go/config ./apps/edge/cmd/edge`를 실행한다.
|
||||
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 코드리뷰 전용 체크리스트
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
|
||||
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.
|
||||
|
||||
- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
|
||||
- [x] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
|
||||
- [x] active `CODE_REVIEW-*-G??.md`를 `code_review_{review_lane}_GNN_N.log`로 아카이브한다.
|
||||
- [x] active `PLAN-*-G??.md`를 `plan_{build_lane}_GNN_M.log`로 아카이브한다.
|
||||
- [x] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하여 plan/review/archive 산출물이 추적 가능한지 확인한다.
|
||||
- [x] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
|
||||
- [x] PASS이면 active task 디렉터리 `agent-task/m-oto-bootstrap-command-issue/01_oto_bootstrap_config/`를 `agent-task/archive/YYYY/MM/m-oto-bootstrap-command-issue/01_oto_bootstrap_config/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
|
||||
- [x] PASS이고 task group이 `m-oto-bootstrap-command-issue`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
|
||||
- [x] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-oto-bootstrap-command-issue/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
|
||||
- [ ] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-{build_lane}-GNN.md`와 `CODE_REVIEW-{review_lane}-GNN.md`를 작성하고 `complete.log`를 작성하지 않는다.
|
||||
- [ ] USER_REVIEW이면 `agent-ops/skills/common/code-review/templates/user-review-template.md` 기준으로 `USER_REVIEW.md`를 작성하고 active `PLAN-*.md`, `CODE_REVIEW-*.md`, `complete.log`를 남기지 않는다.
|
||||
- [ ] USER_REVIEW가 사용자 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
없음.
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
- `patchBootstrapStringFields` 헬퍼 함수를 추가하여 `artifact_base_url`, `oto_bootstrap_script_url`, `oto_release_base_url`을 순서대로 일관되고 결정론적(deterministic order)인 방식으로 YAML 노드에 패치하도록 설계했습니다. 이를 통해 map의 순서 무작위성으로 인해 YAML이 불필요하게 변경되는 현상을 막았습니다.
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
- 상태: 없음
|
||||
- 사유 유형: 없음
|
||||
- 결정 필요: 없음
|
||||
- 차단 근거: 없음
|
||||
- 실행한 검증/명령: 없음
|
||||
- 자동 후속 불가 이유: 없음
|
||||
- 재개 조건: 없음
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
|
||||
- `bootstrap.oto_bootstrap_script_url`과 `bootstrap.oto_release_base_url`이 loader/default/example/test에 모두 반영됐는가.
|
||||
- YAML patch helper가 기존 `artifact_base_url` 동작을 깨지 않고 새 OTO 필드를 보존형으로 추가/갱신하는가.
|
||||
- 이 subtask가 `agent register` command 구현으로 scope를 넓히지 않았는가.
|
||||
|
||||
## 검증 결과
|
||||
|
||||
### CONFIG-1 중간 검증
|
||||
```bash
|
||||
$ go test -count=1 ./packages/go/config
|
||||
ok iop/packages/go/config 0.019s
|
||||
```
|
||||
|
||||
### CONFIG-2 중간 검증
|
||||
```bash
|
||||
$ go test -count=1 ./apps/edge/cmd/edge
|
||||
ok iop/apps/edge/cmd/edge 0.054s
|
||||
```
|
||||
|
||||
### 최종 검증
|
||||
```bash
|
||||
$ go test -count=1 ./packages/go/config ./apps/edge/cmd/edge
|
||||
ok iop/packages/go/config 0.018s
|
||||
ok iop/apps/edge/cmd/edge 0.078s
|
||||
```
|
||||
|
||||
```bash
|
||||
$ go test -count=1 ./packages/go/... ./apps/edge/...
|
||||
? iop/packages/go/auth [no test files]
|
||||
ok iop/packages/go/config 0.021s
|
||||
? iop/packages/go/events [no test files]
|
||||
? iop/packages/go/hostsetup [no test files]
|
||||
? iop/packages/go/jobs [no test files]
|
||||
? iop/packages/go/metadata [no test files]
|
||||
? iop/packages/go/observability [no test files]
|
||||
? iop/packages/go/policy [no test files]
|
||||
? iop/packages/go/version [no test files]
|
||||
ok iop/apps/edge/cmd/edge 0.054s
|
||||
? iop/apps/edge/internal/bootstrap [no test files]
|
||||
? iop/apps/edge/internal/controlplane [no test files]
|
||||
? iop/apps/edge/internal/events [no test files]
|
||||
? iop/apps/edge/internal/input [no test files]
|
||||
ok iop/apps/edge/internal/node 0.015s
|
||||
? iop/apps/edge/internal/openai [no test files]
|
||||
? iop/apps/edge/internal/opsconsole [no test files]
|
||||
? iop/apps/edge/internal/service [no test files]
|
||||
ok iop/apps/edge/internal/transport 0.134s
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?**
|
||||
> If anything is blank, go back and fill it in before saving this file.
|
||||
> Leave review-agent-only sections unchanged.
|
||||
|
||||
## 코드리뷰 결과
|
||||
|
||||
- 종합 판정: PASS
|
||||
- 차원별 평가:
|
||||
- correctness: Pass
|
||||
- completeness: Pass
|
||||
- test coverage: Pass
|
||||
- API contract: Pass
|
||||
- code quality: Pass
|
||||
- plan deviation: Pass
|
||||
- verification trust: Pass
|
||||
- 발견된 문제: 없음
|
||||
- 다음 단계: PASS 종결. `complete.log` 작성 후 task archive로 이동한다.
|
||||
|
||||
리뷰 검증:
|
||||
|
||||
```bash
|
||||
$ go test -count=1 ./packages/go/config ./apps/edge/cmd/edge
|
||||
ok iop/packages/go/config 0.049s
|
||||
ok iop/apps/edge/cmd/edge 0.093s
|
||||
```
|
||||
|
||||
```bash
|
||||
$ go test -count=1 ./apps/edge/internal/controlplane -run TestConnectorStopClosesConnection -v
|
||||
=== RUN TestConnectorStopClosesConnection
|
||||
--- PASS: TestConnectorStopClosesConnection (0.01s)
|
||||
PASS
|
||||
ok iop/apps/edge/internal/controlplane 0.018s
|
||||
```
|
||||
|
||||
```bash
|
||||
$ go test -count=1 ./packages/go/... ./apps/edge/...
|
||||
ok iop/packages/go/audit 0.005s
|
||||
ok iop/packages/go/config 0.016s
|
||||
ok iop/packages/go/hostsetup 0.006s
|
||||
ok iop/packages/go/observability 0.014s
|
||||
ok iop/apps/edge/cmd/edge 0.057s
|
||||
ok iop/apps/edge/internal/bootstrap 0.032s
|
||||
ok iop/apps/edge/internal/controlplane 4.451s
|
||||
ok iop/apps/edge/internal/events 0.009s
|
||||
ok iop/apps/edge/internal/input 0.008s
|
||||
ok iop/apps/edge/internal/input/a2a 0.008s
|
||||
ok iop/apps/edge/internal/node 0.015s
|
||||
ok iop/apps/edge/internal/openai 1.516s
|
||||
ok iop/apps/edge/internal/opsconsole 0.008s
|
||||
ok iop/apps/edge/internal/service 0.007s
|
||||
ok iop/apps/edge/internal/transport 0.018s
|
||||
```
|
||||
|
||||
참고: 첫 `go test -count=1 ./packages/go/... ./apps/edge/...` 재실행에서 이번 변경과 무관한 `apps/edge/internal/controlplane`의 `TestConnectorStopClosesConnection`이 1회 실패했으나, 해당 단일 테스트와 전체 범위 재실행이 이어서 통과했다.
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
# Complete - m-oto-bootstrap-command-issue/01_oto_bootstrap_config
|
||||
|
||||
## 완료 일시
|
||||
|
||||
2026-06-03
|
||||
|
||||
## 요약
|
||||
|
||||
CONFIG plan 0/code-review 0 loop를 PASS로 종결했다. Edge bootstrap OTO URL config 계약과 YAML patch helper 변경이 계획 범위와 검증 기준을 충족했다.
|
||||
|
||||
## 루프 이력
|
||||
|
||||
| Plan | Review | Verdict | 메모 |
|
||||
|------|--------|---------|------|
|
||||
| `plan_local_G05_0.log` | `code_review_local_G05_0.log` | PASS | Required/Suggested 이슈 없음 |
|
||||
|
||||
## 구현/정리 내용
|
||||
|
||||
- `EdgeBootstrapConf`에 `oto_bootstrap_script_url`, `oto_release_base_url` 필드와 empty default를 추가했다.
|
||||
- `configs/edge.yaml` bootstrap 예시에 OTO bootstrap script/release base URL 필드를 추가했다.
|
||||
- `patchBootstrapStringFields` helper로 `artifact_base_url`, `oto_bootstrap_script_url`, `oto_release_base_url` YAML patch 경로를 결정론적으로 통합했다.
|
||||
- config load/default 테스트와 Edge YAML patch 테스트를 추가했다.
|
||||
|
||||
## 최종 검증
|
||||
|
||||
- `go test -count=1 ./packages/go/config ./apps/edge/cmd/edge` - PASS; `iop/packages/go/config`, `iop/apps/edge/cmd/edge` 모두 통과.
|
||||
- `go test -count=1 ./apps/edge/internal/controlplane -run TestConnectorStopClosesConnection -v` - PASS; 첫 전체 범위 실행에서 1회 실패한 무관 control-plane 테스트를 단독 재확인.
|
||||
- `go test -count=1 ./packages/go/... ./apps/edge/...` - PASS; packages/go 및 apps/edge 범위 재실행 통과.
|
||||
|
||||
## 잔여 Nit
|
||||
|
||||
- 없음
|
||||
|
||||
## 후속 작업
|
||||
|
||||
- 없음
|
||||
|
|
@ -0,0 +1,181 @@
|
|||
<!-- task=m-oto-bootstrap-command-issue/01_oto_bootstrap_config plan=0 tag=CONFIG -->
|
||||
|
||||
# Plan - CONFIG
|
||||
|
||||
## 이 파일을 읽는 구현 에이전트에게
|
||||
|
||||
`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션 작성은 필수다. 구현 후 검증을 실행하고 실제 구현 내용과 stdout/stderr를 review stub에 기록한 뒤 active 파일을 그대로 두고 리뷰 준비 상태를 보고한다. 최종 판정, log rename, `complete.log`, archive 이동은 code-review 전용이다. 사용자 결정, 사용자 소유 외부 환경, scope 충돌로 막히면 review stub의 `사용자 리뷰 요청` 섹션에 정확한 결정과 증거를 남기고 멈춘다. 후속 에이전트가 재실행이나 증거 수집으로 해소할 수 있는 검증 공백은 사용자 리뷰 요청이 아니다.
|
||||
|
||||
## 배경
|
||||
|
||||
현재 `node register`는 Node artifact URL만 `bootstrap.artifact_base_url`에 보존한다. OTO bootstrap command는 OTO 소유 script URL과 release base URL을 함께 소비해야 하므로 Edge config가 두 값을 읽고 YAML patch가 보존할 수 있어야 한다. 이 선행 작업은 command 구현 전에 config 계약만 작게 고정한다.
|
||||
|
||||
## 사용자 리뷰 요청 흐름
|
||||
|
||||
구현 중 blocker는 active review stub의 `사용자 리뷰 요청` 섹션에 기록한다. 섹션 형식은 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md`를 따른다. code-review가 요청 정당성을 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정한다.
|
||||
|
||||
## 분석 결과
|
||||
|
||||
### 읽은 파일
|
||||
|
||||
- `agent-roadmap/current.md`
|
||||
- `agent-roadmap/phase/automation-runtime-bridge/PHASE.md`
|
||||
- `agent-roadmap/phase/automation-runtime-bridge/milestones/oto-bootstrap-command-issue.md`
|
||||
- `agent-ops/rules/project/domain/edge/rules.md`
|
||||
- `agent-ops/rules/project/domain/platform-common/rules.md`
|
||||
- `agent-ops/rules/project/domain/testing/rules.md`
|
||||
- `agent-test/local/rules.md`
|
||||
- `agent-test/local/edge-smoke.md`
|
||||
- `agent-test/local/platform-common-smoke.md`
|
||||
- `agent-test/local/testing-smoke.md`
|
||||
- `apps/edge/cmd/edge/main.go`
|
||||
- `apps/edge/cmd/edge/main_test.go`
|
||||
- `packages/go/config/config.go`
|
||||
- `packages/go/config/config_test.go`
|
||||
- `configs/edge.yaml`
|
||||
- `apps/edge/internal/node/mapper.go`
|
||||
- `apps/edge/internal/node/store.go`
|
||||
- `apps/edge/internal/transport/server.go`
|
||||
- `../oto/assets/script/shell/oto_agent_bootstrap.sh`
|
||||
|
||||
### 테스트 환경 규칙
|
||||
|
||||
- `test_env=local`; `agent-test/local/rules.md`를 읽었다.
|
||||
- 적용 profile: `agent-test/local/edge-smoke.md`, `agent-test/local/platform-common-smoke.md`, `agent-test/local/testing-smoke.md`.
|
||||
- 적용 명령: edge 변경은 `go test ./apps/edge/...`, platform-common 변경은 `go test ./packages/go/...`, bootstrap UX 변경은 변경 entrypoint full-cycle 확인을 요구한다.
|
||||
- 이 subtask의 최종 명령은 fresh 실행을 위해 `-count=1`을 사용한다. fallback 없음.
|
||||
|
||||
### 테스트 커버리지 공백
|
||||
|
||||
- `bootstrap.oto_bootstrap_script_url`, `bootstrap.oto_release_base_url` 로딩: 기존 테스트 없음. `packages/go/config/config_test.go`에 default/explicit load 테스트를 추가한다.
|
||||
- YAML patch가 새 bootstrap 필드를 보존/갱신하는 동작: 기존 `artifact_base_url`만 다룬다. `apps/edge/cmd/edge/main_test.go`에 patch roundtrip 테스트를 추가한다.
|
||||
- OTO agent adapterless config check는 작은 선행 변경으로 `TestConfigCheckAcceptsOTOAgentWithoutAdapters`가 추가되어 통과했다.
|
||||
|
||||
### 심볼 참조
|
||||
|
||||
- renamed/removed symbol: none.
|
||||
|
||||
### 분할 판단
|
||||
|
||||
- split decision policy를 plan 파일 선택 전에 평가했다.
|
||||
- shared task group: `m-oto-bootstrap-command-issue`.
|
||||
- `01_oto_bootstrap_config`: 독립 선행 작업. config struct/default/example/YAML patch helper를 다룬다.
|
||||
- `02+01_agent_register_command`: `01` 완료 후 command surface가 새 config 계약을 소비한다.
|
||||
- 이 plan은 predecessor가 없다. sibling `02+01_agent_register_command`는 이 subtask의 `complete.log`를 기다린다.
|
||||
|
||||
### 범위 결정 근거
|
||||
|
||||
- 포함: `packages/go/config`, `configs/edge.yaml`, `apps/edge/cmd/edge`의 YAML patch helper와 테스트.
|
||||
- 제외: `agent register` command 구현, OTO bootstrap script 구현, release asset build/upload, checksum/signature, Control Plane UI.
|
||||
- `../oto`는 script 인자 계약 확인용으로만 읽고 수정하지 않는다.
|
||||
|
||||
### 빌드 등급
|
||||
|
||||
- build=`local-G05`, review=`local-G05`. Config 계약과 YAML patch helper 중심의 bounded 변경이고 Go 테스트로 검증 가능하다.
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] [CONFIG-1] Edge bootstrap config에 OTO script/release URL 필드를 추가하고 load/default/example 테스트를 작성한다.
|
||||
- [ ] [CONFIG-2] YAML patch helper가 `artifact_base_url`, `oto_bootstrap_script_url`, `oto_release_base_url`을 같은 방식으로 보존/갱신하게 만든다.
|
||||
- [ ] [CONFIG-3] `go test -count=1 ./packages/go/config ./apps/edge/cmd/edge`를 실행한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## CONFIG-1 Edge bootstrap config 필드
|
||||
|
||||
### 문제
|
||||
|
||||
`packages/go/config/config.go:86`의 `EdgeBootstrapConf`는 Node artifact URL만 가진다.
|
||||
|
||||
```go
|
||||
86 type EdgeBootstrapConf struct {
|
||||
87 ArtifactBaseURL string `mapstructure:"artifact_base_url" yaml:"artifact_base_url"`
|
||||
88 Listen string `mapstructure:"listen" yaml:"listen"`
|
||||
89 ArtifactDir string `mapstructure:"artifact_dir" yaml:"artifact_dir"`
|
||||
90 }
|
||||
```
|
||||
|
||||
OTO script는 필수 `--release-base-url`을 요구하고 HTTPS를 검증한다: `../oto/assets/script/shell/oto_agent_bootstrap.sh:16`, `../oto/assets/script/shell/oto_agent_bootstrap.sh:82`.
|
||||
|
||||
### 해결 방법
|
||||
|
||||
`EdgeBootstrapConf`에 아래 필드를 추가한다.
|
||||
|
||||
```go
|
||||
OTOBootstrapScriptURL string `mapstructure:"oto_bootstrap_script_url" yaml:"oto_bootstrap_script_url"`
|
||||
OTOReleaseBaseURL string `mapstructure:"oto_release_base_url" yaml:"oto_release_base_url"`
|
||||
```
|
||||
|
||||
`setEdgeDefaults`에는 두 값을 빈 문자열 default로 둔다. `configs/edge.yaml`의 `bootstrap:` 주석과 예시에 두 필드를 추가한다.
|
||||
|
||||
### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `packages/go/config/config.go`에 필드와 defaults를 추가한다.
|
||||
- [ ] `packages/go/config/config_test.go`에 default empty와 explicit load 테스트를 추가한다.
|
||||
- [ ] `configs/edge.yaml`의 `bootstrap:` 예시에 두 값을 빈 문자열로 추가한다.
|
||||
|
||||
### 테스트 작성
|
||||
|
||||
작성한다. `packages/go/config/config_test.go`에 `TestLoadEdge_OTOBootstrapDefaults`와 `TestLoadEdge_OTOBootstrapExplicit`를 추가한다. 목표는 default empty와 YAML explicit 값이 loader/runtime load에서 유지되는지 확인하는 것이다.
|
||||
|
||||
### 중간 검증
|
||||
|
||||
```bash
|
||||
go test -count=1 ./packages/go/config
|
||||
```
|
||||
|
||||
기대 결과: `ok iop/packages/go/config`.
|
||||
|
||||
## CONFIG-2 YAML patch helper 확장
|
||||
|
||||
### 문제
|
||||
|
||||
`apps/edge/cmd/edge/main.go:1163` 이후 `patchYAML`은 `bootstrap.artifact_base_url` 하나만 patch한다. 새 command가 OTO URL을 config에 기록하려면 같은 보존형 patch 경로가 필요하다.
|
||||
|
||||
```go
|
||||
1163 // 2. Patch "bootstrap.artifact_base_url" if bootstrap config has artifact URL
|
||||
1164 if cfg.Bootstrap.ArtifactBaseURL != "" {
|
||||
...
|
||||
1192 urlKey := &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!str", Value: "artifact_base_url"}
|
||||
1193 urlVal := &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!str", Value: cfg.Bootstrap.ArtifactBaseURL}
|
||||
```
|
||||
|
||||
### 해결 방법
|
||||
|
||||
`patchBootstrapStringFields(rootMap, map[string]string)` helper를 추가하고 `artifact_base_url`, `oto_bootstrap_script_url`, `oto_release_base_url` 중 값이 비어 있지 않은 필드만 patch한다. 기존 `artifact_base_url` 동작은 유지한다.
|
||||
|
||||
### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `apps/edge/cmd/edge/main.go`에서 bootstrap patch 로직을 helper로 분리한다.
|
||||
- [ ] `apps/edge/cmd/edge/main_test.go`에 OTO bootstrap fields patch 테스트를 추가한다.
|
||||
|
||||
### 테스트 작성
|
||||
|
||||
작성한다. `apps/edge/cmd/edge/main_test.go`에 `TestPatchYAMLUpdatesOTOBootstrapFields`를 추가한다. 목표는 기존 YAML layout을 보존하면서 `bootstrap.oto_bootstrap_script_url`과 `bootstrap.oto_release_base_url`이 추가/갱신되는지 확인하는 것이다.
|
||||
|
||||
### 중간 검증
|
||||
|
||||
```bash
|
||||
go test -count=1 ./apps/edge/cmd/edge
|
||||
```
|
||||
|
||||
기대 결과: `ok iop/apps/edge/cmd/edge`.
|
||||
|
||||
## 수정 파일 요약
|
||||
|
||||
| 파일 | 항목 |
|
||||
|------|------|
|
||||
| `packages/go/config/config.go` | CONFIG-1 |
|
||||
| `packages/go/config/config_test.go` | CONFIG-1 |
|
||||
| `configs/edge.yaml` | CONFIG-1 |
|
||||
| `apps/edge/cmd/edge/main.go` | CONFIG-2 |
|
||||
| `apps/edge/cmd/edge/main_test.go` | CONFIG-2 |
|
||||
|
||||
## 최종 검증
|
||||
|
||||
```bash
|
||||
go test -count=1 ./packages/go/config ./apps/edge/cmd/edge
|
||||
go test -count=1 ./packages/go/... ./apps/edge/...
|
||||
```
|
||||
|
||||
기대 결과: 모든 package가 exit code 0으로 종료한다.
|
||||
모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
<!-- task=m-oto-bootstrap-command-issue/02+01_agent_register_command plan=0 tag=CLI -->
|
||||
|
||||
# Code Review Reference - CLI
|
||||
|
||||
> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
|
||||
> The task is NOT complete until every implementation-owned section below is filled in.
|
||||
> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving.
|
||||
> Fill implementation-owned sections, then stop with active files in place and report ready for review.
|
||||
> If implementation is blocked by a user-only decision, user-owned external environment prerequisite, or scope conflict, fill `사용자 리뷰 요청` with evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`. Evidence gaps that a follow-up agent can close by rerunning commands or collecting artifacts are normal follow-up issues, not user-review blockers by themselves.
|
||||
> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only, even after compaction/resume.
|
||||
> Follow the ownership table at the bottom of this file for which sections you own.
|
||||
|
||||
## 개요
|
||||
|
||||
date=2026-06-03
|
||||
task=m-oto-bootstrap-command-issue/02+01_agent_register_command, plan=0, tag=CLI
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/automation-runtime-bridge/milestones/oto-bootstrap-command-issue.md`
|
||||
- Task ids:
|
||||
- `agent-register-cmd`: `iop-edge`가 OTO agent record를 만들거나 갱신하는 command 표면을 제공한다.
|
||||
- `node-interface-parity`: OTO agent command/config 표면이 기존 `iop-node` bootstrap의 필드명, flag 의미, 출력 형식, 진단 흐름을 최대한 유지한다.
|
||||
- `bootstrap-output`: command 출력은 한 줄 bootstrap command만 안정적으로 제공하고 token을 불필요하게 재출력하지 않는다.
|
||||
- `oto-args`: 출력 command가 OTO bootstrap 필수 인자 `--edge-url`, `--agent-id`, `--enrollment-token`, `--release-base-url`을 포함한다.
|
||||
- `config-roundtrip`: 생성된 edge config를 `config check`와 runtime load가 읽을 수 있다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 이 파일을 읽는 리뷰 에이전트에게
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다.
|
||||
|
||||
각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
|
||||
리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다.
|
||||
|
||||
1. 판정을 append한다.
|
||||
2. `CODE_REVIEW-{review_lane}-GNN.md` -> `code_review_{review_lane}_GNN_N.log`, `PLAN-{build_lane}-GNN.md` -> `plan_{build_lane}_GNN_M.log`로 아카이브한다.
|
||||
3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/{task_name}/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다. `USER_REVIEW.md`가 사용자 결정으로 완료/PASS 해소되면 code-review가 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log` 작성 후 archive 이동한다.
|
||||
4. PASS이고 task group이 `m-<milestone-slug>`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다.
|
||||
5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다.
|
||||
|
||||
---
|
||||
|
||||
## 구현 항목별 완료 여부
|
||||
|
||||
| 항목 | 완료 여부 |
|
||||
|------|---------|
|
||||
| [CLI-1] `iop-edge agent register [agent-id]` command를 추가하고 OTO agent record를 upsert한다. | [ ] |
|
||||
| [CLI-2] OTO bootstrap URL/release URL/edge URL 정책과 one-line command renderer를 구현한다. | [ ] |
|
||||
| [CLI-3] command help, output, config roundtrip, URL boundary, full-cycle command 검증 테스트를 작성한다. | [ ] |
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] [CLI-1] `iop-edge agent register [agent-id]` command를 추가하고 OTO agent record를 upsert한다.
|
||||
- [ ] [CLI-2] OTO bootstrap URL/release URL/edge URL 정책과 one-line command renderer를 구현한다.
|
||||
- [ ] [CLI-3] command help, output, config roundtrip, URL boundary, full-cycle command 검증 테스트를 작성한다.
|
||||
- [ ] [CLI-4] `go test -count=1 ./apps/edge/cmd/edge ./packages/go/config`와 실제 `go run ./apps/edge/cmd/edge ... agent register ...` full-cycle command를 실행한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## 코드리뷰 전용 체크리스트
|
||||
|
||||
> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
|
||||
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.
|
||||
|
||||
- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
|
||||
- [ ] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
|
||||
- [ ] active `CODE_REVIEW-*-G??.md`를 `code_review_{review_lane}_GNN_N.log`로 아카이브한다.
|
||||
- [ ] active `PLAN-*-G??.md`를 `plan_{build_lane}_GNN_M.log`로 아카이브한다.
|
||||
- [ ] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하여 plan/review/archive 산출물이 추적 가능한지 확인한다.
|
||||
- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
|
||||
- [ ] PASS이면 active task 디렉터리 `agent-task/m-oto-bootstrap-command-issue/02+01_agent_register_command/`를 `agent-task/archive/YYYY/MM/m-oto-bootstrap-command-issue/02+01_agent_register_command/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
|
||||
- [ ] PASS이고 task group이 `m-oto-bootstrap-command-issue`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
|
||||
- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-oto-bootstrap-command-issue/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
|
||||
- [ ] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-{build_lane}-GNN.md`와 `CODE_REVIEW-{review_lane}-GNN.md`를 작성하고 `complete.log`를 작성하지 않는다.
|
||||
- [ ] USER_REVIEW이면 `agent-ops/skills/common/code-review/templates/user-review-template.md` 기준으로 `USER_REVIEW.md`를 작성하고 active `PLAN-*.md`, `CODE_REVIEW-*.md`, `complete.log`를 남기지 않는다.
|
||||
- [ ] USER_REVIEW가 사용자 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다.
|
||||
|
||||
## 계획 대비 변경 사항
|
||||
|
||||
_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._
|
||||
|
||||
## 주요 설계 결정
|
||||
|
||||
_구현 에이전트가 주요 설계 결정 사항을 기록한다._
|
||||
|
||||
## 사용자 리뷰 요청
|
||||
|
||||
_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._
|
||||
|
||||
- 상태: 없음
|
||||
- 사유 유형: 없음
|
||||
- 결정 필요: 없음
|
||||
- 차단 근거: 없음
|
||||
- 실행한 검증/명령: 없음
|
||||
- 자동 후속 불가 이유: 없음
|
||||
- 재개 조건: 없음
|
||||
|
||||
## 리뷰어를 위한 체크포인트
|
||||
|
||||
- `agent-task/m-oto-bootstrap-command-issue/01_oto_bootstrap_config/complete.log`가 존재한 뒤 구현됐는가.
|
||||
- `agent register`가 OTO record를 `agent_kind=oto-agent`로 저장하고 generic node path를 깨지 않는가.
|
||||
- 출력이 정확히 한 줄이고 OTO 필수 인자 4개를 포함하며 token을 추가로 출력하지 않는가.
|
||||
- HTTPS 기본 정책과 explicit local/dev HTTP override가 테스트로 고정됐는가.
|
||||
- `Roadmap Targets`의 5개 task id가 PASS 근거와 일치하는가.
|
||||
|
||||
## 검증 결과
|
||||
|
||||
_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._
|
||||
|
||||
필수 규칙:
|
||||
- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다.
|
||||
- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다.
|
||||
- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다.
|
||||
- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다.
|
||||
- mobile/UI hang, timeout, 또는 2분 무진행은 blind retry를 중단하고 focused rerun 명령과 screenshot/window/UI-tree evidence path를 남기며, 불가능하면 정확한 사유를 남긴다.
|
||||
|
||||
### CLI-1 중간 검증
|
||||
```bash
|
||||
$ go test -count=1 ./apps/edge/cmd/edge
|
||||
(output)
|
||||
```
|
||||
|
||||
### CLI-2 중간 검증
|
||||
```bash
|
||||
$ go test -count=1 ./apps/edge/cmd/edge
|
||||
(output)
|
||||
```
|
||||
|
||||
### CLI-3 중간 검증
|
||||
```bash
|
||||
$ go test -count=1 ./apps/edge/cmd/edge ./packages/go/config
|
||||
(output)
|
||||
```
|
||||
|
||||
### 최종 검증
|
||||
```bash
|
||||
$ go test -count=1 ./apps/edge/cmd/edge ./packages/go/config
|
||||
(output)
|
||||
```
|
||||
|
||||
```bash
|
||||
$ go test -count=1 ./apps/edge/... ./packages/go/...
|
||||
(output)
|
||||
```
|
||||
|
||||
```bash
|
||||
$ tmpdir="$(mktemp -d)"; printf 'server:\n listen: "127.0.0.1:19090"\n advertise_host: "edge.example.test"\nbootstrap:\n oto_bootstrap_script_url: "https://oto.example.test/bootstrap/oto_agent_bootstrap.sh"\n oto_release_base_url: "https://oto.example.test/releases"\nnodes: []\n' > "$tmpdir/edge.yaml"; go run ./apps/edge/cmd/edge --config "$tmpdir/edge.yaml" agent register oto-agent-a --token test-token; go run ./apps/edge/cmd/edge --config "$tmpdir/edge.yaml" config check
|
||||
(output)
|
||||
```
|
||||
|
||||
```bash
|
||||
$ rg --sort path -n "agent register|oto_bootstrap_script_url|oto_release_base_url|--edge-url|--release-base-url" apps/edge/cmd/edge packages/go/config configs agent-roadmap/phase/automation-runtime-bridge/milestones/oto-bootstrap-command-issue.md
|
||||
(output)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?**
|
||||
> If anything is blank, go back and fill it in before saving this file.
|
||||
> Leave review-agent-only sections unchanged.
|
||||
|
|
@ -0,0 +1,267 @@
|
|||
<!-- task=m-oto-bootstrap-command-issue/02+01_agent_register_command plan=0 tag=CLI -->
|
||||
|
||||
# Plan - CLI
|
||||
|
||||
## 이 파일을 읽는 구현 에이전트에게
|
||||
|
||||
`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션 작성은 필수다. 구현 후 검증을 실행하고 실제 구현 내용과 stdout/stderr를 review stub에 기록한 뒤 active 파일을 그대로 두고 리뷰 준비 상태를 보고한다. 최종 판정, log rename, `complete.log`, archive 이동은 code-review 전용이다. 사용자 결정, 사용자 소유 외부 환경, scope 충돌로 막히면 review stub의 `사용자 리뷰 요청` 섹션에 정확한 결정과 증거를 남기고 멈춘다. 후속 에이전트가 재실행이나 증거 수집으로 해소할 수 있는 검증 공백은 사용자 리뷰 요청이 아니다.
|
||||
|
||||
## 배경
|
||||
|
||||
현재 Edge CLI는 generic node용 `node register`만 제공한다. OTO는 같은 token 기반 proto-socket registration path를 재사용하지만 bootstrap command는 OTO script와 release URL 인자를 소비해야 한다. 이 작업은 선행 config 계약이 완료된 뒤 Edge-local `agent register` command를 추가해 OTO agent record와 one-line bootstrap command를 만든다.
|
||||
|
||||
## 사용자 리뷰 요청 흐름
|
||||
|
||||
구현 중 blocker는 active review stub의 `사용자 리뷰 요청` 섹션에 기록한다. 섹션 형식은 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md`를 따른다. code-review가 요청 정당성을 검증하고 실제 `USER_REVIEW.md` 작성 여부를 결정한다.
|
||||
|
||||
## Roadmap Targets
|
||||
|
||||
- Milestone: `agent-roadmap/phase/automation-runtime-bridge/milestones/oto-bootstrap-command-issue.md`
|
||||
- Task ids:
|
||||
- `agent-register-cmd`: `iop-edge`가 OTO agent record를 만들거나 갱신하는 command 표면을 제공한다.
|
||||
- `node-interface-parity`: OTO agent command/config 표면이 기존 `iop-node` bootstrap의 필드명, flag 의미, 출력 형식, 진단 흐름을 최대한 유지한다.
|
||||
- `bootstrap-output`: command 출력은 한 줄 bootstrap command만 안정적으로 제공하고 token을 불필요하게 재출력하지 않는다.
|
||||
- `oto-args`: 출력 command가 OTO bootstrap 필수 인자 `--edge-url`, `--agent-id`, `--enrollment-token`, `--release-base-url`을 포함한다.
|
||||
- `config-roundtrip`: 생성된 edge config를 `config check`와 runtime load가 읽을 수 있다.
|
||||
- Completion mode: check-on-pass
|
||||
|
||||
## 분석 결과
|
||||
|
||||
### 읽은 파일
|
||||
|
||||
- `agent-roadmap/current.md`
|
||||
- `agent-roadmap/phase/automation-runtime-bridge/PHASE.md`
|
||||
- `agent-roadmap/phase/automation-runtime-bridge/milestones/oto-bootstrap-command-issue.md`
|
||||
- `agent-ops/rules/project/domain/edge/rules.md`
|
||||
- `agent-ops/rules/project/domain/platform-common/rules.md`
|
||||
- `agent-ops/rules/project/domain/testing/rules.md`
|
||||
- `agent-test/local/rules.md`
|
||||
- `agent-test/local/edge-smoke.md`
|
||||
- `agent-test/local/platform-common-smoke.md`
|
||||
- `agent-test/local/testing-smoke.md`
|
||||
- `apps/edge/cmd/edge/main.go`
|
||||
- `apps/edge/cmd/edge/main_test.go`
|
||||
- `packages/go/config/config.go`
|
||||
- `packages/go/config/config_test.go`
|
||||
- `configs/edge.yaml`
|
||||
- `apps/edge/internal/node/mapper.go`
|
||||
- `apps/edge/internal/node/store.go`
|
||||
- `apps/edge/internal/transport/server.go`
|
||||
- `../oto/assets/script/shell/oto_agent_bootstrap.sh`
|
||||
- `../oto/lib/oto/agent/edge_registration_client.dart`
|
||||
- `../oto/test/oto_agent_config_test.dart`
|
||||
|
||||
### 테스트 환경 규칙
|
||||
|
||||
- `test_env=local`; `agent-test/local/rules.md`를 읽었다.
|
||||
- 적용 profile: `agent-test/local/edge-smoke.md`, `agent-test/local/platform-common-smoke.md`, `agent-test/local/testing-smoke.md`.
|
||||
- 적용 명령: `go test ./apps/edge/...`, `go test ./packages/go/...`, bootstrap/install UX 변경의 실제 entrypoint full-cycle command.
|
||||
- 이 plan의 Go 검증은 fresh 실행을 위해 `-count=1`을 사용한다. fallback 없음.
|
||||
|
||||
### 테스트 커버리지 공백
|
||||
|
||||
- OTO `agent register` command: 기존 테스트 없음. 새 CLI tests가 필요하다.
|
||||
- OTO bootstrap one-line output: 기존 node output tests만 있다. 새 exact-output test가 필요하다.
|
||||
- OTO 필수 인자 포함: 기존 테스트 없음. command output assertion으로 검증한다.
|
||||
- HTTP script URL gating: 기존 테스트 없음. boundary test가 필요하다.
|
||||
- Config roundtrip: 작은 선행 변경으로 adapterless OTO config check는 통과했고, 이 plan은 generated config가 `config check`와 `config.LoadEdge`를 통과하는 테스트를 추가해야 한다.
|
||||
|
||||
### 심볼 참조
|
||||
|
||||
- renamed/removed symbol: none.
|
||||
|
||||
### 분할 판단
|
||||
|
||||
- split decision policy를 plan 파일 선택 전에 평가했다.
|
||||
- shared task group: `m-oto-bootstrap-command-issue`.
|
||||
- sibling subtasks:
|
||||
- `01_oto_bootstrap_config`: config struct/default/example/YAML patch 선행 계약.
|
||||
- `02+01_agent_register_command`: 이 plan. command surface가 `01`의 config fields와 patch helper를 소비한다.
|
||||
- predecessor `01`: active plan exists at `agent-task/m-oto-bootstrap-command-issue/01_oto_bootstrap_config/PLAN-local-G05.md`; `complete.log` is not present yet. Runtime must wait for predecessor completion before implementing this task.
|
||||
|
||||
### 범위 결정 근거
|
||||
|
||||
- 포함: `apps/edge/cmd/edge` command surface, CLI tests, generated temp config full-cycle check.
|
||||
- 제외: OTO bootstrap script 변경, OTO release asset build/upload, checksum/signature enforcement, Control Plane remote issue UI, OTO online smoke. Online smoke는 후속 Milestone이다.
|
||||
- `../oto`는 script and config parser contract 확인용으로만 읽고 수정하지 않는다.
|
||||
|
||||
### 빌드 등급
|
||||
|
||||
- build=`cloud-G07`, review=`cloud-G07`. Public CLI workflow, one-line bootstrap output, URL policy, config mutation, full-cycle command evidence가 함께 걸려 terminal/CLI reasoning과 verification trust가 중요하다.
|
||||
|
||||
## 의존 관계 및 구현 순서
|
||||
|
||||
`02+01_agent_register_command`는 디렉터리 이름 기준으로 predecessor index `01`에 의존한다. `agent-task/m-oto-bootstrap-command-issue/01_oto_bootstrap_config/complete.log`가 생긴 뒤 구현한다. 추가 dependency는 없다.
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
- [ ] [CLI-1] `iop-edge agent register [agent-id]` command를 추가하고 OTO agent record를 upsert한다.
|
||||
- [ ] [CLI-2] OTO bootstrap URL/release URL/edge URL 정책과 one-line command renderer를 구현한다.
|
||||
- [ ] [CLI-3] command help, output, config roundtrip, URL boundary, full-cycle command 검증 테스트를 작성한다.
|
||||
- [ ] [CLI-4] `go test -count=1 ./apps/edge/cmd/edge ./packages/go/config`와 실제 `go run ./apps/edge/cmd/edge ... agent register ...` full-cycle command를 실행한다.
|
||||
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
|
||||
|
||||
## CLI-1 Agent register command surface
|
||||
|
||||
### 문제
|
||||
|
||||
`apps/edge/cmd/edge/main.go:66`의 root command는 `nodeCmd()`만 등록하고 specialized agent command가 없다.
|
||||
|
||||
```go
|
||||
66 root.AddCommand(serveCmd(), consoleCmd(), versionCmd(), configCmd(), envCmd(), setupCmd(), bootstrapCmd(), nodeCmd(), nodesCmd(), smokeCmd())
|
||||
```
|
||||
|
||||
`nodeRegisterCmd`는 generic node adapter/runtime 설정을 전제로 한다: `apps/edge/cmd/edge/main.go:913`.
|
||||
|
||||
### 해결 방법
|
||||
|
||||
`agentCmd()`와 `agentRegisterCmd()`를 추가하고 root에 `agentCmd()`를 등록한다. Command shape:
|
||||
|
||||
```text
|
||||
iop-edge agent register <agent-id> --bootstrap-script-url <url> --release-base-url <url>
|
||||
```
|
||||
|
||||
Flags:
|
||||
|
||||
- `--alias` default `<agent-id>`
|
||||
- `--token` default generated UUID, same meaning as `node register --token`
|
||||
- `--edge-url` default `addressFor(resolveAdvertiseHost(cfg.Server.AdvertiseHost).Host, cfg.Server.Listen)`
|
||||
- `--bootstrap-script-url` overrides and persists `bootstrap.oto_bootstrap_script_url`
|
||||
- `--release-base-url` overrides and persists `bootstrap.oto_release_base_url`
|
||||
- `--allow-insecure-bootstrap-url` allows local/dev `http://` script URL only when explicit
|
||||
|
||||
Upsert record:
|
||||
|
||||
```go
|
||||
config.NodeDefinition{
|
||||
ID: agentID,
|
||||
Alias: alias,
|
||||
Token: token,
|
||||
AgentKind: config.AgentKindOTOAgent,
|
||||
}
|
||||
```
|
||||
|
||||
Do not enable Node adapters for OTO records.
|
||||
|
||||
### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `apps/edge/cmd/edge/main.go`에 `agentCmd`, `agentRegisterCmd`, OTO-specific flag variables를 추가한다.
|
||||
- [ ] `apps/edge/cmd/edge/main.go` root command에 `agentCmd()`를 등록한다.
|
||||
- [ ] Existing `node register` behavior는 변경하지 않는다.
|
||||
|
||||
### 테스트 작성
|
||||
|
||||
작성한다. `apps/edge/cmd/edge/main_test.go`에 `TestAgentRegisterUpsertsOTOAgentRecord`를 추가한다. 목표는 OTO record가 `agent_kind: oto-agent`, alias, token을 보존하고 adapter 없이 config check/load를 통과하는지 확인하는 것이다.
|
||||
|
||||
### 중간 검증
|
||||
|
||||
```bash
|
||||
go test -count=1 ./apps/edge/cmd/edge
|
||||
```
|
||||
|
||||
기대 결과: `ok iop/apps/edge/cmd/edge`.
|
||||
|
||||
## CLI-2 One-line OTO bootstrap renderer and URL policy
|
||||
|
||||
### 문제
|
||||
|
||||
기존 `node register` 출력은 Node script와 positional token만 렌더링한다: `apps/edge/cmd/edge/main.go:1101`.
|
||||
|
||||
```go
|
||||
1101 bootstrapURL := universalBootstrapScriptURL(artifactURL)
|
||||
1102 fmt.Fprintf(cmd.OutOrStdout(), "curl -fsSL %s | bash -s %s\n", bootstrapURL, nodeDef.Token)
|
||||
```
|
||||
|
||||
OTO script는 named 필수 인자 4개를 요구한다: `../oto/assets/script/shell/oto_agent_bootstrap.sh:16`, `../oto/assets/script/shell/oto_agent_bootstrap.sh:76`.
|
||||
|
||||
### 해결 방법
|
||||
|
||||
`renderOTOBootstrapCommand(opts)` helper를 추가한다. Output must be exactly one line and include:
|
||||
|
||||
```text
|
||||
curl -fsSL <bootstrap-script-url> | bash -s -- --edge-url <edge-url> --agent-id <agent-id> --enrollment-token <token> --release-base-url <release-base-url> --agent-alias <alias>
|
||||
```
|
||||
|
||||
Validation:
|
||||
|
||||
- script URL and release base URL must come from flag or config; missing values fail before YAML write.
|
||||
- `release-base-url` must start with `https://`.
|
||||
- script URL must start with `https://` unless `--allow-insecure-bootstrap-url` is set. `http://` is accepted only with that explicit flag.
|
||||
- token appears only as the `--enrollment-token` value in the one-line command.
|
||||
|
||||
### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `apps/edge/cmd/edge/main.go`에 OTO URL resolver/validator를 추가한다.
|
||||
- [ ] `apps/edge/cmd/edge/main.go`에 one-line renderer를 추가한다.
|
||||
- [ ] `apps/edge/cmd/edge/main_test.go`에 exact output and URL boundary tests를 추가한다.
|
||||
|
||||
### 테스트 작성
|
||||
|
||||
작성한다. Tests:
|
||||
|
||||
- `TestAgentRegisterPrintsOneLineOTOCommand`
|
||||
- `TestAgentRegisterRejectsInsecureBootstrapURLWithoutFlag`
|
||||
- `TestAgentRegisterRejectsInsecureReleaseBaseURL`
|
||||
- `TestAgentRegisterAllowsInsecureBootstrapURLWithExplicitFlag`
|
||||
|
||||
### 중간 검증
|
||||
|
||||
```bash
|
||||
go test -count=1 ./apps/edge/cmd/edge
|
||||
```
|
||||
|
||||
기대 결과: `ok iop/apps/edge/cmd/edge`.
|
||||
|
||||
## CLI-3 Help, config roundtrip, and full-cycle command evidence
|
||||
|
||||
### 문제
|
||||
|
||||
Root help currently advertises only `node register`: `apps/edge/cmd/edge/main.go:49`, and help tests assert the same at `apps/edge/cmd/edge/main_test.go:1099`. The milestone requires OTO agent command/config parity and generated config roundtrip.
|
||||
|
||||
### 해결 방법
|
||||
|
||||
Update help without replacing the existing generic node flow. Add OTO agent register as an optional specialized agent path. Tests must assert command discovery and no legacy script path leakage.
|
||||
|
||||
Generated config roundtrip test should run command against a temp `edge.yaml`, then:
|
||||
|
||||
- parse YAML and assert OTO URL config fields are persisted,
|
||||
- call `config.LoadEdge`,
|
||||
- run `config check`,
|
||||
- assert output is exactly one command line.
|
||||
|
||||
### 수정 파일 및 체크리스트
|
||||
|
||||
- [ ] `apps/edge/cmd/edge/main.go` root/help examples mention `agent register` as OTO specialized path.
|
||||
- [ ] `apps/edge/cmd/edge/main_test.go` updates command discovery/help tests.
|
||||
- [ ] `apps/edge/cmd/edge/main_test.go` adds generated config roundtrip test.
|
||||
|
||||
### 테스트 작성
|
||||
|
||||
작성한다. `TestHelpDiscoversEdgeLocalDevFlow`에 `agent register` command discovery를 추가하고, a full roundtrip test를 새로 추가한다.
|
||||
|
||||
### 중간 검증
|
||||
|
||||
```bash
|
||||
go test -count=1 ./apps/edge/cmd/edge ./packages/go/config
|
||||
```
|
||||
|
||||
기대 결과: both packages pass.
|
||||
|
||||
## 수정 파일 요약
|
||||
|
||||
| 파일 | 항목 |
|
||||
|------|------|
|
||||
| `apps/edge/cmd/edge/main.go` | CLI-1, CLI-2, CLI-3 |
|
||||
| `apps/edge/cmd/edge/main_test.go` | CLI-1, CLI-2, CLI-3 |
|
||||
|
||||
## 최종 검증
|
||||
|
||||
```bash
|
||||
go test -count=1 ./apps/edge/cmd/edge ./packages/go/config
|
||||
go test -count=1 ./apps/edge/... ./packages/go/...
|
||||
tmpdir="$(mktemp -d)"; printf 'server:\n listen: "127.0.0.1:19090"\n advertise_host: "edge.example.test"\nbootstrap:\n oto_bootstrap_script_url: "https://oto.example.test/bootstrap/oto_agent_bootstrap.sh"\n oto_release_base_url: "https://oto.example.test/releases"\nnodes: []\n' > "$tmpdir/edge.yaml"; go run ./apps/edge/cmd/edge --config "$tmpdir/edge.yaml" agent register oto-agent-a --token test-token; go run ./apps/edge/cmd/edge --config "$tmpdir/edge.yaml" config check
|
||||
rg --sort path -n "agent register|oto_bootstrap_script_url|oto_release_base_url|--edge-url|--release-base-url" apps/edge/cmd/edge packages/go/config configs agent-roadmap/phase/automation-runtime-bridge/milestones/oto-bootstrap-command-issue.md
|
||||
```
|
||||
|
||||
기대 결과: Go tests pass; full-cycle command prints one bootstrap line then config check prints `OK`; search output shows command/config/milestone anchors.
|
||||
모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
|
||||
|
|
@ -880,7 +880,7 @@ func validateEdgeConfig(cfg *config.EdgeConfig) error {
|
|||
name = fmt.Sprintf("index %d", i)
|
||||
}
|
||||
|
||||
if !n.Adapters.Ollama.Enabled && !n.Adapters.CLI.Enabled && !n.Adapters.Vllm.Enabled {
|
||||
if n.AgentKind != config.AgentKindOTOAgent && !n.Adapters.Ollama.Enabled && !n.Adapters.CLI.Enabled && !n.Adapters.Vllm.Enabled {
|
||||
return fmt.Errorf("node %q: at least one adapter must be enabled", name)
|
||||
}
|
||||
if n.Adapters.Ollama.Enabled && n.Adapters.Ollama.BaseURL == "" {
|
||||
|
|
@ -1160,41 +1160,18 @@ func patchYAML(data []byte, cfg *config.EdgeConfig) ([]byte, error) {
|
|||
rootMap.Content = append(rootMap.Content, keyNode, nodesValNode)
|
||||
}
|
||||
|
||||
// 2. Patch "bootstrap.artifact_base_url" if bootstrap config has artifact URL
|
||||
// 2. Patch bootstrap fields if any are present in the config
|
||||
fieldsToPatch := make(map[string]string)
|
||||
if cfg.Bootstrap.ArtifactBaseURL != "" {
|
||||
foundBootstrap := false
|
||||
for i := 0; i < len(rootMap.Content); i += 2 {
|
||||
if rootMap.Content[i].Value == "bootstrap" {
|
||||
bootstrapVal := rootMap.Content[i+1]
|
||||
if bootstrapVal.Kind == yaml.MappingNode {
|
||||
foundURL := false
|
||||
for j := 0; j < len(bootstrapVal.Content); j += 2 {
|
||||
if bootstrapVal.Content[j].Value == "artifact_base_url" {
|
||||
bootstrapVal.Content[j+1].Value = cfg.Bootstrap.ArtifactBaseURL
|
||||
bootstrapVal.Content[j+1].Tag = "!!str"
|
||||
foundURL = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !foundURL {
|
||||
urlKey := &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!str", Value: "artifact_base_url"}
|
||||
urlVal := &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!str", Value: cfg.Bootstrap.ArtifactBaseURL}
|
||||
bootstrapVal.Content = append(bootstrapVal.Content, urlKey, urlVal)
|
||||
}
|
||||
}
|
||||
foundBootstrap = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !foundBootstrap {
|
||||
bootstrapKey := &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!str", Value: "bootstrap"}
|
||||
bootstrapVal := &yaml.Node{Kind: yaml.MappingNode, Tag: "!!map"}
|
||||
urlKey := &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!str", Value: "artifact_base_url"}
|
||||
urlVal := &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!str", Value: cfg.Bootstrap.ArtifactBaseURL}
|
||||
bootstrapVal.Content = append(bootstrapVal.Content, urlKey, urlVal)
|
||||
rootMap.Content = append(rootMap.Content, bootstrapKey, bootstrapVal)
|
||||
}
|
||||
fieldsToPatch["artifact_base_url"] = cfg.Bootstrap.ArtifactBaseURL
|
||||
}
|
||||
if cfg.Bootstrap.OTOBootstrapScriptURL != "" {
|
||||
fieldsToPatch["oto_bootstrap_script_url"] = cfg.Bootstrap.OTOBootstrapScriptURL
|
||||
}
|
||||
if cfg.Bootstrap.OTOReleaseBaseURL != "" {
|
||||
fieldsToPatch["oto_release_base_url"] = cfg.Bootstrap.OTOReleaseBaseURL
|
||||
}
|
||||
patchBootstrapStringFields(rootMap, fieldsToPatch)
|
||||
|
||||
out, err := yaml.Marshal(&doc)
|
||||
if err != nil {
|
||||
|
|
@ -1203,6 +1180,55 @@ func patchYAML(data []byte, cfg *config.EdgeConfig) ([]byte, error) {
|
|||
return out, nil
|
||||
}
|
||||
|
||||
func patchBootstrapStringFields(rootMap *yaml.Node, fields map[string]string) {
|
||||
if len(fields) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
var bootstrapVal *yaml.Node
|
||||
foundBootstrap := false
|
||||
for i := 0; i < len(rootMap.Content); i += 2 {
|
||||
if rootMap.Content[i].Value == "bootstrap" {
|
||||
bootstrapVal = rootMap.Content[i+1]
|
||||
foundBootstrap = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !foundBootstrap {
|
||||
bootstrapKey := &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!str", Value: "bootstrap"}
|
||||
bootstrapVal = &yaml.Node{Kind: yaml.MappingNode, Tag: "!!map"}
|
||||
rootMap.Content = append(rootMap.Content, bootstrapKey, bootstrapVal)
|
||||
}
|
||||
|
||||
if bootstrapVal.Kind != yaml.MappingNode {
|
||||
return
|
||||
}
|
||||
|
||||
// Iterate in a deterministic order
|
||||
keys := []string{"artifact_base_url", "oto_bootstrap_script_url", "oto_release_base_url"}
|
||||
for _, k := range keys {
|
||||
v, ok := fields[k]
|
||||
if !ok || v == "" {
|
||||
continue
|
||||
}
|
||||
foundField := false
|
||||
for j := 0; j < len(bootstrapVal.Content); j += 2 {
|
||||
if bootstrapVal.Content[j].Value == k {
|
||||
bootstrapVal.Content[j+1].Value = v
|
||||
bootstrapVal.Content[j+1].Tag = "!!str"
|
||||
foundField = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !foundField {
|
||||
keyNode := &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!str", Value: k}
|
||||
valNode := &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!str", Value: v}
|
||||
bootstrapVal.Content = append(bootstrapVal.Content, keyNode, valNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func nodesCmd() *cobra.Command {
|
||||
c := &cobra.Command{
|
||||
Use: "nodes",
|
||||
|
|
|
|||
|
|
@ -639,6 +639,35 @@ nodes:
|
|||
}
|
||||
}
|
||||
|
||||
func TestConfigCheckAcceptsOTOAgentWithoutAdapters(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
cfgPath := filepath.Join(dir, "edge.yaml")
|
||||
|
||||
yamlStr := `server:
|
||||
listen: "0.0.0.0:9090"
|
||||
nodes:
|
||||
- id: "oto-agent-a"
|
||||
alias: "oto-a"
|
||||
token: "oto-token-a"
|
||||
agent_kind: "oto-agent"
|
||||
`
|
||||
if err := os.WriteFile(cfgPath, []byte(yamlStr), 0o600); err != nil {
|
||||
t.Fatalf("write yaml: %v", err)
|
||||
}
|
||||
|
||||
root := rootCmd()
|
||||
var out bytes.Buffer
|
||||
root.SetOut(&out)
|
||||
root.SetErr(&out)
|
||||
root.SetArgs([]string{"config", "check", "--config", cfgPath})
|
||||
if err := root.Execute(); err != nil {
|
||||
t.Fatalf("expected OTO agent config to check without adapters: %v\n%s", err, out.String())
|
||||
}
|
||||
if !strings.Contains(out.String(), "OK "+cfgPath) {
|
||||
t.Fatalf("expected config check OK for OTO agent config, got %q", out.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeRegisterUpsertsYAMLAndOutputsBootstrap(t *testing.T) {
|
||||
// Mock token generator
|
||||
origGen := tokenGenerator
|
||||
|
|
@ -1304,3 +1333,51 @@ func TestSmokeOpenAICommandFailure(t *testing.T) {
|
|||
t.Errorf("unexpected error message: %v", err3)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPatchYAMLUpdatesOTOBootstrapFields(t *testing.T) {
|
||||
origYAML := `server:
|
||||
listen: "0.0.0.0:9090"
|
||||
bootstrap:
|
||||
artifact_base_url: "http://example.com/artifacts"
|
||||
`
|
||||
cfg := &config.EdgeConfig{
|
||||
Bootstrap: config.EdgeBootstrapConf{
|
||||
ArtifactBaseURL: "http://example.com/artifacts",
|
||||
OTOBootstrapScriptURL: "https://example.com/bootstrap.sh",
|
||||
OTOReleaseBaseURL: "https://example.com/releases",
|
||||
},
|
||||
}
|
||||
|
||||
patched, err := patchYAML([]byte(origYAML), cfg)
|
||||
if err != nil {
|
||||
t.Fatalf("patchYAML: %v", err)
|
||||
}
|
||||
|
||||
var doc struct {
|
||||
Server struct {
|
||||
Listen string `yaml:"listen"`
|
||||
} `yaml:"server"`
|
||||
Bootstrap struct {
|
||||
ArtifactBaseURL string `yaml:"artifact_base_url"`
|
||||
OTOBootstrapScriptURL string `yaml:"oto_bootstrap_script_url"`
|
||||
OTOReleaseBaseURL string `yaml:"oto_release_base_url"`
|
||||
} `yaml:"bootstrap"`
|
||||
}
|
||||
|
||||
if err := yaml.Unmarshal(patched, &doc); err != nil {
|
||||
t.Fatalf("unmarshal patched: %v", err)
|
||||
}
|
||||
|
||||
if doc.Server.Listen != "0.0.0.0:9090" {
|
||||
t.Errorf("server.listen was modified: %q", doc.Server.Listen)
|
||||
}
|
||||
if doc.Bootstrap.ArtifactBaseURL != "http://example.com/artifacts" {
|
||||
t.Errorf("artifact_base_url = %q", doc.Bootstrap.ArtifactBaseURL)
|
||||
}
|
||||
if doc.Bootstrap.OTOBootstrapScriptURL != "https://example.com/bootstrap.sh" {
|
||||
t.Errorf("oto_bootstrap_script_url = %q", doc.Bootstrap.OTOBootstrapScriptURL)
|
||||
}
|
||||
if doc.Bootstrap.OTOReleaseBaseURL != "https://example.com/releases" {
|
||||
t.Errorf("oto_release_base_url = %q", doc.Bootstrap.OTOReleaseBaseURL)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ server:
|
|||
# field channel is provisioned.
|
||||
bootstrap:
|
||||
artifact_base_url: ""
|
||||
oto_bootstrap_script_url: ""
|
||||
oto_release_base_url: ""
|
||||
|
||||
tls:
|
||||
enabled: false
|
||||
|
|
|
|||
|
|
@ -84,9 +84,11 @@ type EdgeServerConf struct {
|
|||
}
|
||||
|
||||
type EdgeBootstrapConf struct {
|
||||
ArtifactBaseURL string `mapstructure:"artifact_base_url" yaml:"artifact_base_url"`
|
||||
Listen string `mapstructure:"listen" yaml:"listen"`
|
||||
ArtifactDir string `mapstructure:"artifact_dir" yaml:"artifact_dir"`
|
||||
ArtifactBaseURL string `mapstructure:"artifact_base_url" yaml:"artifact_base_url"`
|
||||
Listen string `mapstructure:"listen" yaml:"listen"`
|
||||
ArtifactDir string `mapstructure:"artifact_dir" yaml:"artifact_dir"`
|
||||
OTOBootstrapScriptURL string `mapstructure:"oto_bootstrap_script_url" yaml:"oto_bootstrap_script_url"`
|
||||
OTOReleaseBaseURL string `mapstructure:"oto_release_base_url" yaml:"oto_release_base_url"`
|
||||
}
|
||||
|
||||
type EdgeOpenAIConf struct {
|
||||
|
|
@ -267,6 +269,8 @@ func setEdgeDefaults(v *viper.Viper) {
|
|||
v.SetDefault("server.listen", "0.0.0.0:9090")
|
||||
v.SetDefault("bootstrap.listen", "0.0.0.0:18080")
|
||||
v.SetDefault("bootstrap.artifact_dir", "artifacts")
|
||||
v.SetDefault("bootstrap.oto_bootstrap_script_url", "")
|
||||
v.SetDefault("bootstrap.oto_release_base_url", "")
|
||||
v.SetDefault("openai.enabled", false)
|
||||
v.SetDefault("openai.listen", "0.0.0.0:8080")
|
||||
v.SetDefault("openai.adapter", "ollama")
|
||||
|
|
|
|||
|
|
@ -722,6 +722,48 @@ logging:
|
|||
}
|
||||
}
|
||||
|
||||
func TestLoadEdge_OTOBootstrapDefaults(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
f := filepath.Join(dir, "edge.yaml")
|
||||
if err := os.WriteFile(f, []byte("server:\n listen: \"0.0.0.0:9090\"\n"), 0o600); err != nil {
|
||||
t.Fatalf("write yaml: %v", err)
|
||||
}
|
||||
cfg, err := config.LoadEdge(f)
|
||||
if err != nil {
|
||||
t.Fatalf("load: %v", err)
|
||||
}
|
||||
if cfg.Bootstrap.OTOBootstrapScriptURL != "" {
|
||||
t.Errorf("expected empty bootstrap.oto_bootstrap_script_url by default, got %q", cfg.Bootstrap.OTOBootstrapScriptURL)
|
||||
}
|
||||
if cfg.Bootstrap.OTOReleaseBaseURL != "" {
|
||||
t.Errorf("expected empty bootstrap.oto_release_base_url by default, got %q", cfg.Bootstrap.OTOReleaseBaseURL)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadEdge_OTOBootstrapExplicit(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
f := filepath.Join(dir, "edge.yaml")
|
||||
yaml := `server:
|
||||
listen: "0.0.0.0:9090"
|
||||
bootstrap:
|
||||
oto_bootstrap_script_url: "https://example.com/bootstrap.sh"
|
||||
oto_release_base_url: "https://example.com/releases"
|
||||
`
|
||||
if err := os.WriteFile(f, []byte(yaml), 0o600); err != nil {
|
||||
t.Fatalf("write yaml: %v", err)
|
||||
}
|
||||
cfg, err := config.LoadEdge(f)
|
||||
if err != nil {
|
||||
t.Fatalf("load: %v", err)
|
||||
}
|
||||
if cfg.Bootstrap.OTOBootstrapScriptURL != "https://example.com/bootstrap.sh" {
|
||||
t.Errorf("bootstrap.oto_bootstrap_script_url=%q", cfg.Bootstrap.OTOBootstrapScriptURL)
|
||||
}
|
||||
if cfg.Bootstrap.OTOReleaseBaseURL != "https://example.com/releases" {
|
||||
t.Errorf("bootstrap.oto_release_base_url=%q", cfg.Bootstrap.OTOReleaseBaseURL)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadEdge_A2ADefaults(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
f := filepath.Join(dir, "edge.yaml")
|
||||
|
|
|
|||
Loading…
Reference in a new issue