diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e787148..155c99d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,11 +4,11 @@ on: pull_request: push: branches: ["main"] - release: - types: [published] + tags: + - 'v*' permissions: - contents: read + contents: write jobs: test: @@ -34,19 +34,19 @@ jobs: docker: name: Build & Push Docker image (release only) runs-on: ubuntu-latest - if: github.event_name == 'release' && github.event.action == 'published' + if: startsWith(github.ref, 'refs/tags/') steps: - name: Checkout (release tag) uses: actions/checkout@v4 with: - ref: ${{ github.event.release.tag_name }} + ref: ${{ github.ref_name }} - name: Set image tag (VERSION) from release tag id: meta shell: bash run: | set -euo pipefail - echo "version=${{ github.event.release.tag_name }}" >> "$GITHUB_OUTPUT" + echo "version=${{ github.ref_name }}" >> "$GITHUB_OUTPUT" - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 @@ -72,3 +72,11 @@ jobs: VERSION: ${{ steps.meta.outputs.version }} run: | echo "Pushed image: ${REGISTRY}/browser-ui:${VERSION}" + + - name: Create GitHub Release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh release create "${{ github.ref_name }}" \ + --title "${{ github.ref_name }}" \ + --generate-notes \ No newline at end of file diff --git a/pkg/collector/collector.go b/pkg/collector/collector.go index 281e0a9..6cc0de0 100644 --- a/pkg/collector/collector.go +++ b/pkg/collector/collector.go @@ -13,6 +13,7 @@ import ( "github.com/alcounit/seleniferous/v2/pkg/store" "github.com/alcounit/selenosis/v2/pkg/ipuuid" + browserv1 "github.com/alcounit/browser-controller/apis/browser/v1" logctx "github.com/alcounit/browser-controller/pkg/log" corev1 "k8s.io/api/core/v1" ) @@ -34,6 +35,24 @@ func (c *Collector) Run(ctx context.Context) error { ctx, cancel := context.WithCancel(context.Background()) defer cancel() + browsers, err := c.client.ListBrowsers(ctx, c.namespace) + if err != nil { + return err + + } + + for _, browser := range browsers { + sessionId, err := parseIp(browser.Status.PodIP) + if err != nil { + log.Error().Err(err).Str("sessionId", sessionId).Msgf("sessionId not found") + return errors.New("failed to convert IP to UUID") + } + + storeSession(sessionId, browser, c) + log.Info().Str("sessionId", sessionId).Msg("add session to store") + + } + stream, err := c.client.Events(ctx, c.namespace) if err != nil { return err @@ -65,19 +84,10 @@ func (c *Collector) Run(ctx context.Context) error { return errors.New("failed to convert IP to UUID") } - sess := &types.Session{ - SessionId: sessionId, - BrowserId: browserEvent.Browser.Name, - BrowserIP: browserEvent.Browser.Status.PodIP, - BrowserName: browserEvent.Browser.Spec.BrowserName, - BrowserVersion: browserEvent.Browser.Spec.BrowserVersion, - StartTime: browserEvent.Browser.CreationTimestamp.DeepCopy(), - Phase: corev1.PodPhase(browserEvent.Browser.Status.Phase), - } - eventType := strings.ToLower(string(browserEvent.EventType)) + storeSession(sessionId, browserEvent.Browser, c) - c.store.Set(browserEvent.Browser.Name, sess) - log.Info().Str("eventType", eventType).Str("sessionId", sess.SessionId).Msg("add/update session in store") + eventType := strings.ToLower(string(browserEvent.EventType)) + log.Info().Str("eventType", eventType).Str("sessionId", sessionId).Msg("add/update session in store") continue } @@ -98,3 +108,16 @@ func parseIp(ip string) (string, error) { return rawId.String(), err } + +func storeSession(sessionId string, browser *browserv1.Browser, c *Collector) { + sess := &types.Session{ + SessionId: sessionId, + BrowserId: browser.Name, + BrowserIP: browser.Status.PodIP, + BrowserName: browser.Spec.BrowserName, + BrowserVersion: browser.Spec.BrowserVersion, + StartTime: browser.CreationTimestamp.DeepCopy(), + Phase: corev1.PodPhase(browser.Status.Phase), + } + c.store.Set(browser.Name, sess) +} diff --git a/pkg/collector/collector_test.go b/pkg/collector/collector_test.go index e44a2ae..102d2cf 100644 --- a/pkg/collector/collector_test.go +++ b/pkg/collector/collector_test.go @@ -28,8 +28,10 @@ func (s *fakeStream) Errors() <-chan error { return s.errorsCh } func (s *fakeStream) Close() { s.closed = true } type fakeClient struct { - stream *fakeStream - err error + stream *fakeStream + err error + browsers []*browserv1.Browser + listErr error } func (c *fakeClient) Events(ctx context.Context, namespace string, opts ...client.EventsOption) (client.BrowserEventStream, error) { @@ -39,6 +41,10 @@ func (c *fakeClient) Events(ctx context.Context, namespace string, opts ...clien return c.stream, nil } +func (c *fakeClient) ListBrowsers(context.Context, string) ([]*browserv1.Browser, error) { + return c.browsers, c.listErr +} + func (c *fakeClient) CreateBrowser(context.Context, string, *browserv1.Browser) (*browserv1.Browser, error) { panic("not used") } @@ -48,9 +54,6 @@ func (c *fakeClient) GetBrowser(context.Context, string, string) (*browserv1.Bro func (c *fakeClient) DeleteBrowser(context.Context, string, string) error { panic("not used") } -func (c *fakeClient) ListBrowsers(context.Context, string) ([]*browserv1.Browser, error) { - panic("not used") -} func newBrowserEvent(eventType event.EventType, name, podIP string) *event.BrowserEvent { now := metav1.NewTime(time.Unix(0, 0).UTC()) @@ -176,6 +179,59 @@ func TestCollectorRunInvalidIP(t *testing.T) { } } +func TestCollectorRunListBrowsersError(t *testing.T) { + cl := &fakeClient{listErr: errors.New("list error")} + col := NewCollector(cl, "default", store.NewDefaultStore(), nil) + + err := col.Run(context.Background()) + if err == nil || err.Error() != "list error" { + t.Fatalf("expected list error, got %v", err) + } +} + +func TestCollectorRunListBrowsersPopulatesStore(t *testing.T) { + stream := &fakeStream{ + eventsCh: make(chan *event.BrowserEvent), + errorsCh: make(chan error), + } + close(stream.eventsCh) + + now := metav1.NewTime(time.Unix(0, 0).UTC()) + existing := &browserv1.Browser{ + ObjectMeta: metav1.ObjectMeta{ + Name: "browser-existing", + CreationTimestamp: now, + }, + Spec: browserv1.BrowserSpec{ + BrowserName: "chrome", + BrowserVersion: "123", + }, + Status: browserv1.BrowserStatus{ + PodIP: "127.0.0.1", + Phase: corev1.PodRunning, + }, + } + + st := store.NewDefaultStore() + cl := &fakeClient{stream: stream, browsers: []*browserv1.Browser{existing}} + col := NewCollector(cl, "default", st, nil) + + col.Run(context.Background()) //nolint:errcheck + + val, ok := st.Get("browser-existing") + if !ok { + t.Fatalf("expected browser-existing to be in store after ListBrowsers") + } + sess, ok := val.(*types.Session) + if !ok { + t.Fatalf("expected stored value to be *types.Session") + } + expectedID, _ := ipuuid.IPToUUID(net.ParseIP("127.0.0.1")) + if sess.SessionId != expectedID.String() { + t.Fatalf("expected sessionId %s, got %s", expectedID.String(), sess.SessionId) + } +} + func TestCollectorRunAddsSession(t *testing.T) { stream := &fakeStream{ eventsCh: make(chan *event.BrowserEvent, 1),