Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package hawk-apiserver for openSUSE:Factory checked in at 2026-11-02 16:39:52 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/hawk-apiserver (Old) and /work/SRC/openSUSE:Factory/.hawk-apiserver.new.1255 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "hawk-apiserver" Mon Nov 2 16:39:52 2026 rev:16 rq:1394422 version:0.6.0+git.1793612600.48f2e53 Changes: -------- --- /work/SRC/openSUSE:Factory/hawk-apiserver/hawk-apiserver.changes 2026-10-31 23:09:28.315656935 +0100 +++ /work/SRC/openSUSE:Factory/.hawk-apiserver.new.1255/hawk-apiserver.changes 2026-11-02 16:40:45.494216846 +0100 @@ -1,0 +2,6 @@ +Mon Nov 2 09:48:17 UTC 2026 - Aleksei Burlakov <[email protected]> + +- Version 0.6.0+git.1793612600.48f2e53: + * Dev: implement /cib/live/resources/types menu + +------------------------------------------------------------------- Old: ---- hawk-apiserver-0.5.0+git.1793384507.8daa0a1.tar.gz New: ---- hawk-apiserver-0.6.0+git.1793612600.48f2e53.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ hawk-apiserver.spec ++++++ --- /var/tmp/diff_new_pack.C51Wl0/_old 2026-11-02 16:40:46.882275002 +0100 +++ /var/tmp/diff_new_pack.C51Wl0/_new 2026-11-02 16:40:46.883275044 +0100 @@ -20,7 +20,7 @@ %define www_base %{_datadir}/hawk Name: hawk-apiserver -Version: 0.5.0+git.1793384507.8daa0a1 +Version: 0.6.0+git.1793612600.48f2e53 Release: 0 Summary: Web server and API provider for Hawk License: GPL-3.0-or-later ++++++ _service ++++++ --- /var/tmp/diff_new_pack.C51Wl0/_old 2026-11-02 16:40:46.929276971 +0100 +++ /var/tmp/diff_new_pack.C51Wl0/_new 2026-11-02 16:40:46.932277097 +0100 @@ -3,7 +3,7 @@ <param name="url">https://github.com/ClusterLabs/hawk-apiserver</param> <param name="scm">git</param> <param name="exclude">.git</param> - <param name="versionformat">0.5.0+git.%ct.%h</param> + <param name="versionformat">0.6.0+git.%ct.%h</param> <param name="revision">master</param> </service> <service name="recompress" mode="disabled"> ++++++ hawk-apiserver-0.5.0+git.1793384507.8daa0a1.tar.gz -> hawk-apiserver-0.6.0+git.1793612600.48f2e53.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hawk-apiserver-0.5.0+git.1793384507.8daa0a1/api/resource_types.go new/hawk-apiserver-0.6.0+git.1793612600.48f2e53/api/resource_types.go --- old/hawk-apiserver-0.5.0+git.1793384507.8daa0a1/api/resource_types.go 1970-01-01 01:00:00.000000000 +0100 +++ new/hawk-apiserver-0.6.0+git.1793612600.48f2e53/api/resource_types.go 2026-11-02 10:43:20.000000000 +0100 @@ -0,0 +1,107 @@ +package api + +import ( + "context" + "net/http" + "os/exec" + "time" +) + +type resourceType struct { + Name string + Path string + Icon string + Help string +} + +func resourceTypes(ctx context.Context, crm string) []resourceType { + types := []resourceType{ + { + Name: "Primitive", + Path: "primitives", + Icon: "file", + Help: "Primitive resources are the most basic type of resource. Define an ID and " + + "select the resource agent class, provider and type, then configure its " + + "parameters. For example, an IP address agent can manage a floating IP address.", + }, + { + Name: "Group", + Path: "groups", + Icon: "users", + Help: "Groups contain resources that run together, start in order and stop in reverse " + + "order. Define an ID and select the member resources. For example, group an IP " + + "address, file system and Web server so they fail over together.", + }, + { + Name: "Clone", + Path: "clones", + Icon: "copy", + Help: "Clones run instances of a resource on multiple cluster nodes. Define an ID and " + + "select a primitive or group as the child resource. Instances may behave " + + "identically or use node-specific configuration.", + }, + /* Multi-state is deprecated + { + Name: "Multi-state", + Path: "masters", + Icon: "superscript", + Help: "Promotable resources are specialized clones whose instances can operate in two " + + "modes. A subset of instances can be promoted. Define an ID and select the " + + "child resource. DRBD is an example of a promotable resource.", + }, + */ + { + Name: "Tag", + Path: "tags", + Icon: "tag", + Help: "Tags refer to multiple resources without adding colocation or ordering " + + "relationships. Define an ID and select the objects to reference. A tag can be " + + "used to start or stop related resources with a single command.", + }, + } + // Match Rails' Util.has_feature?(:rsc_template), with a bounded probe. + ctx, cancel := context.WithTimeout(ctx, 3*time.Second) + defer cancel() + if exec.CommandContext(ctx, crm, "configure", "help", "rsc_template").Run() == nil { + types = append(types, resourceType{ + Name: "Template", + Path: "templates", + Icon: "file-lines", + Help: "Resource templates provide reusable configuration for similar resources. " + + "Primitives referencing a template inherit its operations, parameters, meta " + + "attributes and utilization attributes. Values defined on the primitive " + + "override inherited values.", + }) + } + return append(types, resourceType{ + Name: "Alert", + Path: "alerts", + Icon: "bullhorn", + Help: "Alerts run external agents when cluster events occur, such as node failures or " + + "resources starting and stopping. Configure the agent and its recipients, " + + "parameters and meta attributes. Recipients can be addresses, file names or " + + "other values understood by the agent.", + }) +} + +func ResourceTypesHandler(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/cib/live/resources/types" && r.URL.Path != "/cib/live/resources/types/" { + http.NotFound(w, r) + return + } + if r.Method != http.MethodGet && r.Method != http.MethodHead { + w.Header().Set("Allow", "GET, HEAD") + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + data := map[string]any{ + "Title": "Add Resource", + "ResourceTypes": resourceTypes(r.Context(), "/usr/sbin/crm"), + } + // This flash is only for backward compatibility with Ruby redirects. + if r.URL.Query().Get("flash") == "deleted" { + data["AlertType"] = "success" + data["AlertMessage"] = "Resource deleted successfully" + } + renderTemplate(w, "resource_types", data) +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hawk-apiserver-0.5.0+git.1793384507.8daa0a1/api/resource_types_test.go new/hawk-apiserver-0.6.0+git.1793612600.48f2e53/api/resource_types_test.go --- old/hawk-apiserver-0.5.0+git.1793384507.8daa0a1/api/resource_types_test.go 1970-01-01 01:00:00.000000000 +0100 +++ new/hawk-apiserver-0.6.0+git.1793612600.48f2e53/api/resource_types_test.go 2026-11-02 10:43:20.000000000 +0100 @@ -0,0 +1,55 @@ +package api + +import ( + "bytes" + "context" + "html/template" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestResourceTypesWithoutCrm(t *testing.T) { + types := resourceTypes(context.Background(), filepath.Join(t.TempDir(), "missing-crm")) + require.Len(t, types, 5) + for _, item := range types { + require.NotEqual(t, "templates", item.Path) + require.NotEqual(t, "masters", item.Path) + } + require.Equal(t, "alerts", types[len(types)-1].Path) +} + +func TestResourceTypesWithTemplates(t *testing.T) { + crm := filepath.Join(t.TempDir(), "crm") + require.NoError(t, os.WriteFile(crm, []byte("#!/bin/sh\n[ \"$1\" = configure ] && [ \"$2\" = help ] && [ \"$3\" = rsc_template ]\n"), 0755)) + types := resourceTypes(context.Background(), crm) + require.Len(t, types, 6) + tmpl, err := template.ParseFiles("../templates/layout.html", "../templates/resource_types.html") + require.NoError(t, err) + var body bytes.Buffer + require.NoError(t, tmpl.ExecuteTemplate(&body, "layout.html", map[string]any{"Title": "Add Resource", "ResourceTypes": types})) + for _, item := range types { + require.NotEqual(t, "masters", item.Path) + require.Contains(t, body.String(), `href="/cib/live/`+item.Path+`/new"`) + require.Contains(t, body.String(), `id="help-`+item.Path+`"`) + } + require.Contains(t, body.String(), `href="/cib/live/resources"`) +} + +func TestResourceTypesHandlerRejectsOtherRequests(t *testing.T) { + for _, tc := range []struct { + method, path string + status int + }{ + {http.MethodPost, "/cib/live/resources/types", http.StatusMethodNotAllowed}, + {http.MethodGet, "/cib/live/resources/types/unknown", http.StatusNotFound}, + } { + response := httptest.NewRecorder() + ResourceTypesHandler(response, httptest.NewRequest(tc.method, tc.path, nil)) + require.Equal(t, tc.status, response.Code) + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hawk-apiserver-0.5.0+git.1793384507.8daa0a1/main.go new/hawk-apiserver-0.6.0+git.1793612600.48f2e53/main.go --- old/hawk-apiserver-0.5.0+git.1793384507.8daa0a1/main.go 2026-10-30 19:21:47.000000000 +0100 +++ new/hawk-apiserver-0.6.0+git.1793612600.48f2e53/main.go 2026-11-02 10:43:20.000000000 +0100 @@ -131,6 +131,8 @@ // Register BOTH /cib/live/foo and /cib/live/foo/ to avoid conflicts with Ruby mux.HandleFunc("/cib/live/commands", authMiddleware(api.CommandsHandler)) mux.HandleFunc("/cib/live/commands/", authMiddleware(api.CommandsHandler)) + mux.HandleFunc("/cib/live/resources/types", authMiddleware(api.ResourceTypesHandler)) + mux.HandleFunc("/cib/live/resources/types/", authMiddleware(api.ResourceTypesHandler)) mux.HandleFunc("/cib/live/primitives", authMiddleware(api.ResourceEditHandler)) mux.HandleFunc("/cib/live/primitives/", authMiddleware(api.ResourceEditHandler)) mux.HandleFunc("/cib/live/clones", authMiddleware(api.CloneEditHandler)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hawk-apiserver-0.5.0+git.1793384507.8daa0a1/static/css/resource_types.css new/hawk-apiserver-0.6.0+git.1793612600.48f2e53/static/css/resource_types.css --- old/hawk-apiserver-0.5.0+git.1793384507.8daa0a1/static/css/resource_types.css 1970-01-01 01:00:00.000000000 +0100 +++ new/hawk-apiserver-0.6.0+git.1793612600.48f2e53/static/css/resource_types.css 2026-11-02 10:43:20.000000000 +0100 @@ -0,0 +1,59 @@ +.resource-types .resource-type-list { + margin-top: 30px; +} + +.resource-types .resource-type-list > li + li { + margin-top: 20px; +} + +.resource-types .resource-type-list .resource-type-link { + display: flex; + align-items: center; + min-height: 57px; + padding: 0; + border: 1px solid #ddd; + border-radius: 3px; + background: linear-gradient(#fafafa, #f5f5f5); + color: #444; + font-size: 16px; + text-align: left; + white-space: normal; +} + +.resource-types .resource-type-link:hover, +.resource-types .resource-type-link:focus { + background: #eee; +} + +.resource-type-icon { + display: flex; + align-items: center; + justify-content: center; + align-self: stretch; + flex: 0 0 56px; + min-height: 55px; + border-right: 1px solid #ddd; + background: #e9e9e9; + font-size: 18px; +} + +.resource-type-name { + padding: 12px 14px; +} + +.resource-type-add { + margin-left: auto; + margin-right: 20px; + font-size: 19px; +} + +.resource-type-link:hover .resource-type-add, +.resource-type-link:focus .resource-type-add { + color: #00c081; +} + +@media (max-width: 767px) { + .resource-types.edit-layout { + flex-direction: column; + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hawk-apiserver-0.5.0+git.1793384507.8daa0a1/static/js/commands.js new/hawk-apiserver-0.6.0+git.1793612600.48f2e53/static/js/commands.js --- old/hawk-apiserver-0.5.0+git.1793384507.8daa0a1/static/js/commands.js 2026-10-30 19:21:47.000000000 +0100 +++ new/hawk-apiserver-0.6.0+git.1793612600.48f2e53/static/js/commands.js 2026-11-02 10:43:20.000000000 +0100 @@ -1,23 +1,9 @@ "use strict"; -<<<<<<< Updated upstream -(() => { - const container = document.getElementById("commands"); - const status = document.getElementById("commands-status"); - const error = document.getElementById("commands-error"); - const refresh = document.getElementById("commands-refresh"); - - async function loadCommands() { - refresh.disabled = true; - container.setAttribute("aria-busy", "true"); - error.hidden = true; - status.textContent = "Loading commands…"; -======= async function loadCommands() { const container = document.getElementById("commands"); const error = document.getElementById("commands-error"); ->>>>>>> Stashed changes try { // Keep the Rails command log as the source of truth during the UI port. const response = await fetch("/cib/live/commands.json", { @@ -45,29 +31,13 @@ error.textContent = `Unable to load command log. ${cause.message}`; error.hidden = false; } finally { -<<<<<<< Updated upstream - refresh.disabled = false; - container.setAttribute("aria-busy", "false"); -======= container.setAttribute("aria-busy", "false"); ->>>>>>> Stashed changes } } -<<<<<<< Updated upstream - // Open the sidebar section containing the current page. - document.getElementById("monitoringMenu").classList.remove("in"); - document.getElementById("troubleshootMenu").classList.add("in"); - const link = document.querySelector('a[href="/cib/live/commands"]'); - link.setAttribute("aria-current", "page"); - refresh.addEventListener("click", loadCommands); - loadCommands(); -})(); -======= // Open the sidebar section containing the current page. document.getElementById("monitoringMenu").classList.remove("in"); document.getElementById("troubleshootMenu").classList.add("in"); const link = document.querySelector('a[href="/cib/live/commands"]'); link.setAttribute("aria-current", "page"); loadCommands(); ->>>>>>> Stashed changes diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hawk-apiserver-0.5.0+git.1793384507.8daa0a1/static/js/resource_types.js new/hawk-apiserver-0.6.0+git.1793612600.48f2e53/static/js/resource_types.js --- old/hawk-apiserver-0.5.0+git.1793384507.8daa0a1/static/js/resource_types.js 1970-01-01 01:00:00.000000000 +0100 +++ new/hawk-apiserver-0.6.0+git.1793612600.48f2e53/static/js/resource_types.js 2026-11-02 10:43:20.000000000 +0100 @@ -0,0 +1,24 @@ +// This flash is only for backward compatibility with Ruby redirects. +// Consume the query parameter so reloading does not show the message again. +const resourceTypesURL = new URL(window.location.href); +if (resourceTypesURL.searchParams.get("flash") === "deleted") { + resourceTypesURL.searchParams.delete("flash"); + history.replaceState({}, "", resourceTypesURL); +} + +function showResourceHelp(type) { + document.querySelectorAll(".resource-type-help").forEach(section => { + section.hidden = section.id !== `help-${type}`; + }); +} + +const resourceTypeLinks = document.querySelectorAll("[data-resource-type]"); +resourceTypeLinks.forEach(link => { + link.addEventListener("mouseenter", () => showResourceHelp(link.dataset.resourceType)); + link.addEventListener("focus", () => showResourceHelp(link.dataset.resourceType)); +}); +if (resourceTypeLinks.length) showResourceHelp(resourceTypeLinks[0].dataset.resourceType); + +document.getElementById("monitoringMenu").classList.remove("in"); +document.getElementById("configurationMenu").classList.add("in"); +document.querySelector('a[href="/cib/live/resources/types"]').setAttribute("aria-current", "page"); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hawk-apiserver-0.5.0+git.1793384507.8daa0a1/templates/commands.html new/hawk-apiserver-0.6.0+git.1793612600.48f2e53/templates/commands.html --- old/hawk-apiserver-0.5.0+git.1793384507.8daa0a1/templates/commands.html 2026-10-30 19:21:47.000000000 +0100 +++ new/hawk-apiserver-0.6.0+git.1793612600.48f2e53/templates/commands.html 2026-11-02 10:43:20.000000000 +0100 @@ -1,15 +1,10 @@ {{ define "content" }} <script src="/static/js/commands.js" defer></script> -<div class="container-fluid"> +<div class="edit-root"> <h1><i class="fa fa-calendar-days page" aria-hidden="true"></i> Command Log</h1> <div class="panel panel-default"> <div class="panel-body"> <p class="text-muted">Lists crm commands recently executed by hawk, most recent first.</p> -<<<<<<< Updated upstream - <button id="commands-refresh" class="btn btn-default" type="button">Refresh</button> - <p id="commands-status" role="status" aria-live="polite">Loading commands…</p> -======= ->>>>>>> Stashed changes <div id="commands-error" class="alert alert-danger" role="alert" hidden></div> <div id="commands" class="commands" aria-busy="true"></div> </div> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/hawk-apiserver-0.5.0+git.1793384507.8daa0a1/templates/resource_types.html new/hawk-apiserver-0.6.0+git.1793612600.48f2e53/templates/resource_types.html --- old/hawk-apiserver-0.5.0+git.1793384507.8daa0a1/templates/resource_types.html 1970-01-01 01:00:00.000000000 +0100 +++ new/hawk-apiserver-0.6.0+git.1793612600.48f2e53/templates/resource_types.html 2026-11-02 10:43:20.000000000 +0100 @@ -0,0 +1,38 @@ +{{ define "content" }} +<link rel="stylesheet" href="/static/css/primitive_edit.css"> +<link rel="stylesheet" href="/static/css/resource_types.css"> +<script src="/static/js/classes/XAlert.js"></script> +<script src="/static/js/resource_types.js" defer></script> +<div class="edit-layout resource-types"> + <div class="edit-left"> + {{ if .AlertMessage }} + <x-alert type="{{ .AlertType }}" message="{{ .AlertMessage }}" visible="true"></x-alert> + {{ end }} + <h1><i class="fas fa-code page" aria-hidden="true"></i> Add Resource + <a href="/cib/live/resources" class="btn btn-default pull-right"><i class="fas fa-list" aria-hidden="true"></i> List</a> + </h1> + <ul class="nav nav-pills nav-stacked resource-type-list"> + {{ range .ResourceTypes }} + <li> + <a class="resource-type-link" href="/cib/live/{{ .Path }}/new" data-resource-type="{{ .Path }}"{{ if eq .Path "clones" }} data-help-filter=".clone"{{ else if eq .Path "groups" }} data-help-filter=".group"{{ end }} aria-describedby="help-{{ .Path }}"> + <span class="resource-type-icon" aria-hidden="true">{{ if eq .Path "masters" }}<span>x<sup>2</sup></span>{{ else }}<i class="{{ if or (eq .Path "primitives") (eq .Path "clones") }}far{{ else }}fas{{ end }} fa-{{ .Icon }}"></i>{{ end }}</span> + <span class="resource-type-name">{{ .Name }}</span> + <i class="fas fa-circle-plus resource-type-add" aria-hidden="true"></i> + </a> + </li> + {{ end }} + </ul> + </div> + <aside class="edit-right" aria-label="Resource help"> + <h2>Resources</h2> + <p>Configure a cluster resource for each service or application that you want to make highly available, such as a Web server, database, file system or virtual machine.</p> + <p>Set up the service or application first, and ensure its requirements are met on every cluster node before configuring and starting the resource.</p> + {{ range .ResourceTypes }} + <section id="help-{{ .Path }}" class="resource-type-help"> + <h3>{{ .Name }}</h3> + <p>{{ .Help }}</p> + </section> + {{ end }} + </aside> +</div> +{{ end }} ++++++ vendor.tar.gz ++++++
