Skip to content

Commit

Permalink
Initial work towards subscriptions
Browse files Browse the repository at this point in the history
  • Loading branch information
SludgeGirl committed Sep 19, 2024
1 parent 2c24b8d commit 89535e0
Show file tree
Hide file tree
Showing 15 changed files with 577 additions and 61 deletions.
17 changes: 0 additions & 17 deletions org.cockpit-project.starter-kit.metainfo.xml

This file was deleted.

17 changes: 17 additions & 0 deletions org.cockpit-project.subscriptions.metainfo.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="addon">
<id>org.cockpit_project.subscriptions</id>
<metadata_license>CC0-1.0</metadata_license>
<name>Subscriptions</name>
<summary>Scaffolding for a cockpit module</summary>
<description>
<p>
Scaffolding for a cockpit module.
</p>
</description>
<extends>org.cockpit_project.subscriptions</extends>
<launchable type="cockpit-manifest">subscriptions</launchable>
<url type="homepage">https://github.com/cockpit-project/subscriptions</url>
<url type="bugtracker">https://github.com/cockpit-project/subscriptions/issues</url>
<update_contact>cockpit-devel_AT_lists.fedorahosted.org</update_contact>
</component>
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"name": "starter-kit",
"name": "subscriptions",
"description": "Scaffolding for a cockpit module",
"type": "module",
"main": "index.js",
"repository": "git@github.com:cockpit/starter-kit.git",
"repository": "git@github.com:cockpit/subscriptions.git",
"author": "",
"license": "LGPL-2.1",
"engines": {
Expand Down
6 changes: 3 additions & 3 deletions packit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# To use this, enable Packit-as-a-service in GitHub: https://packit.dev/docs/packit-as-a-service/
# See https://packit.dev/docs/configuration/ for the format of this file

specfile_path: cockpit-starter-kit.spec
specfile_path: cockpit-subscriptions.spec
# use the nicely formatted release description from our upstream release, instead of git shortlog
copy_upstream_release_description: true

Expand All @@ -12,12 +12,12 @@ srpm_build_deps:

actions:
post-upstream-clone:
- make cockpit-starter-kit.spec
- make cockpit-subscriptions.spec
# replace Source1 manually, as create-archive: can't handle multiple tarballs
- make node-cache
- sh -c 'sed -i "/^Source1:/ s/https:.*/$(ls *-node*.tar.xz)/" cockpit-*.spec'
create-archive: make dist
# starter-kit.git has no release tags; your project can drop this once you have a release
# subscriptions.git has no release tags; your project can drop this once you have a release
get-current-version: make print-version

jobs:
Expand Down
1 change: 1 addition & 0 deletions po/LINGUAS
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
de
1 change: 1 addition & 0 deletions src/app.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
@use "@patternfly/patternfly/patternfly-addons";
@use "page.scss";

p {
Expand Down
170 changes: 133 additions & 37 deletions src/app.tsx
Original file line number Diff line number Diff line change
@@ -1,48 +1,144 @@
/*
* This file is part of Cockpit.
*
* Copyright (C) 2017 Red Hat, Inc.
*
* Cockpit is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* Cockpit is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
*/

import React, { useEffect, useState } from 'react';
import { Alert } from "@patternfly/react-core/dist/esm/components/Alert/index.js";
import React, { useCallback, useEffect, useState } from 'react';
import { Card, CardBody, CardTitle } from "@patternfly/react-core/dist/esm/components/Card/index.js";

import cockpit from 'cockpit';

const _ = cockpit.gettext;
import { Page, PageSection, PageSectionVariants } from '@patternfly/react-core';
import { Backend, Extension, Subscription } from './backends/backend';
import { TransactionalUpdate } from './backends/transactional-update';
import { SuseConnect } from './backends/suseconnect';
import { RegisterCodeForm, RegisterFormData } from './components/register_code_form';
import { SubscriptionList } from './components/subscription_list';
import { useDialogs } from 'dialogs';
import { RebootDialog } from './components/reboot_dialog';

export const Application = () => {
const [hostname, setHostname] = useState(_("Unknown"));
const [backend, setBackend] = useState<Backend | null>(null);
const [loadingSubscriptions, setLoadingSubscriptions] = useState<boolean>(true);
const [loadingExtensions, setLoadingExtensions] = useState<boolean>(true);
const [subscriptions, setSubscriptions] = useState<Subscription[]>([]);
const [unregisteredSubscriptions, setUnregisteredSubscriptions] = useState<Extension[]>([]);
const [formData, setFormData] = useState<RegisterFormData>({
registrationCode: "",
email: "",
product: "",
});

const Dialogs = useDialogs();

useEffect(() => {
cockpit.spawn(["test", "-e", "/sbin/transactional-update"], { err: "ignore" })
.then(() => setBackend(new TransactionalUpdate()))
.catch(() => {
cockpit.spawn(["test", "-e", "/usr/bin/suseconnect"], { err: "ignore" })
.then(() => setBackend(new SuseConnect()));
});
}, [setBackend]);

const updateSubscriptions = useCallback(() => {
if (backend === null) {
return;
}
setLoadingSubscriptions(true);

// handle get subscriptions
backend.getSubscriptions()
.then((subscriptions) => {
setSubscriptions(subscriptions);
setLoadingSubscriptions(false);
});

backend.getExtensions()
.then((subscriptions) => {
setUnregisteredSubscriptions(subscriptions);
setLoadingExtensions(false);
});
}, [backend, setLoadingSubscriptions, setSubscriptions, setUnregisteredSubscriptions, setLoadingExtensions]);

useEffect(() => {
const hostname = cockpit.file('/etc/hostname');
hostname.watch(content => setHostname(content?.trim() ?? ""));
return hostname.close;
}, []);
updateSubscriptions();
}, [updateSubscriptions, backend]);

const registerProduct = async (): Promise<[boolean, string]> => {
console.debug("registering", formData);
const result = await backend?.register(formData.registrationCode, formData.email, formData.product).then((result) => {
if (result[0]) {
if (result[1].includes("Please reboot your machine")) {
// Show reboot modal
Dialogs.show(<RebootDialog />);
return result;
}

updateSubscriptions();
return result;
}

return result;
});

return result || [false, ""];
};

const deactivateProduct = (subscription: Subscription | Extension): void => {
console.log("deregistering", subscription.identifier);
setLoadingSubscriptions(true);
backend?.deregister([subscription.identifier, subscription.version, subscription.arch].join("/"))
.then(async (output) => {
console.log(output);
if (output.includes("Can not deregister base product")) {
console.log("deregistering base");
output = await backend.deregister();
console.log("base deregistered");
}
if (output.includes("Please reboot your machine")) {
Dialogs.show(<RebootDialog />);
}
console.log("deregistered");
setLoadingSubscriptions(false);
updateSubscriptions();
});
};

const activateProduct = (subscription: Subscription | Extension): void => {
console.log("activating", subscription.identifier);
setLoadingExtensions(true);
backend?.register("", "", [subscription.identifier, subscription.version, subscription.arch].join("/"))
.then(async (result) => {
if (result[0]) {
if (result[1].includes("Please reboot your machine")) {
// Show reboot modal
Dialogs.show(<RebootDialog />);
}
}
console.log("activated");
setLoadingExtensions(false);
updateSubscriptions();
});
};

return (
<Card>
<CardTitle>Starter Kit</CardTitle>
<CardBody>
<Alert
variant="info"
title={ cockpit.format(_("Running on $0"), hostname) }
/>
</CardBody>
</Card>
<Page>
<PageSection variant={PageSectionVariants.light}>
<Card>
<CardTitle>Register a new subscription</CardTitle>
<CardBody>
<RegisterCodeForm submitCallback={registerProduct} formData={formData} setFormData={setFormData} />
</CardBody>
</Card>
<Card>
<CardTitle>Registered Subscriptions</CardTitle>
<CardBody>
<SubscriptionList subscriptions={subscriptions} loading={loadingSubscriptions} deactivate={deactivateProduct} />
</CardBody>
</Card>
{unregisteredSubscriptions.length
? <Card>
<CardTitle>Available Extensions</CardTitle>
<CardBody>
<SubscriptionList subscriptions={unregisteredSubscriptions} loading={loadingExtensions} activate={activateProduct} />
</CardBody>
</Card>
: ""}
</PageSection>
</Page>
);
};
56 changes: 56 additions & 0 deletions src/backends/backend.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
type Extension = {
name: string,
identifier: string,
version: string,
arch: string,
activated: boolean,
available: boolean,
free: boolean
extensions: Extension[],
expires_at?: string,
}

enum SubscriptionStatus {
Active = "Active",
Expired = "Expired",
Unregistered = "Unregistered",
}

type Subscription = {
name?: string,
identifier: string,
version: string,
arch: string,
status: string,
regcode?: string,
starts_at?: string,
expires_at?: string,
subscription_status?: SubscriptionStatus,
type?: string,
extensions?: Extension[],
}

// Cockpits typescript types don't contain this
// Just a more slimmed down BasicError, might be worth upstreaming
type CockpitSpawnError = {
message: string,
problem: string | null,
exit_status: number | null,
exit_signal: string | null,
}

enum SUSEConnectExitCodes {
ZyppBusy = 7,
}

interface Backend {
getSubscriptions(): Promise<Subscription[]>;

getExtensions(): Promise<Extension[]>;

register(reg_code?: string, email?: string, product?: string): Promise<[boolean, string]>;

deregister(product?: string): Promise<string>;
}

export { Backend, Subscription, SubscriptionStatus, Extension, CockpitSpawnError, SUSEConnectExitCodes };
Loading

0 comments on commit 89535e0

Please sign in to comment.