Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 7 additions & 17 deletions core/frontend/src/components/cloud/CloudTrayMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -197,13 +197,15 @@ import { FilebrowserFile } from '@/types/filebrowser'
import { InstalledExtensionData, RunningContainer } from '@/types/kraken'
import back_axios from '@/utils/api'
import { prettifySize } from '@/utils/helper_functions'
import {
installOrUpdateMajorTom,
MAJOR_TOM_IDENTIFIER,
} from '@/utils/major_tom'
import PullTracker from '@/utils/pull_tracker'

const KRAKEN_API_URL = '/kraken/v1.0'

const MAJOR_TOM_CLOUD_URL = 'https://blueos.cloud/major_tom/install'

const MAJOR_TOM_EXTENSION_IDENTIFIER = 'blueos.major_tom'
const MAJOR_TOM_EXTENSION_IDENTIFIER = MAJOR_TOM_IDENTIFIER

const BLUEOS_CLOUD_URL = 'https://app.blueos.cloud'

Expand Down Expand Up @@ -711,11 +713,6 @@ export default Vue.extend({
async cleanMajorTomFileToken(): Promise<void> {
await filebrowser.deleteFile(MAJOR_TOM_CLOUD_TOKEN_FILE)
},
async fetchMajorTomData(): Promise<InstalledExtensionData> {
const data = await axios.get(MAJOR_TOM_CLOUD_URL)

return data.data as InstalledExtensionData
},
async fetchMajorTomFileToken(): Promise<string | undefined> {
try {
const response = await fetch(await filebrowser.singleFileRelativeURL(MAJOR_TOM_CLOUD_TOKEN_FILE))
Expand Down Expand Up @@ -808,17 +805,10 @@ export default Vue.extend({
)

try {
const majorTomData = await this.fetchMajorTomData()

this.pull_show_modal = true

await back_axios({
method: 'POST',
url: `${KRAKEN_API_URL}/extension/install`,
data: majorTomData,
onDownloadProgress: (progressEvent) => {
this.pull_tracker?.digestNewData(progressEvent.event)
},
await installOrUpdateMajorTom((progressEvent) => {
this.pull_tracker?.digestNewData(progressEvent.event)
})
} catch (error) {
this.setOperationError('Failed to install Major Tom.', error)
Expand Down
10 changes: 10 additions & 0 deletions core/frontend/src/components/utils/PullProgress.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@
</v-card-title>

<v-card-text>
<p
v-if="step !== ''"
class="text-subtitle-2 mb-1 mt-3"
>
{{ step }}
</p>
<v-progress-linear
:buffer-value="download"
:value="extraction"
Expand Down Expand Up @@ -80,6 +86,10 @@ export default Vue.extend({
type: String,
required: true,
},
step: {
type: String,
default: '',
},
},
data() {
return {
Expand Down
99 changes: 89 additions & 10 deletions core/frontend/src/components/version-chooser/VersionChooser.vue
Original file line number Diff line number Diff line change
Expand Up @@ -222,12 +222,21 @@
</v-alert>
</v-card>
</v-col>
<pull-progress
:progress="major_tom_pull_output"
:show="show_major_tom_progress"
:download="major_tom_download_percentage"
:extraction="major_tom_extraction_percentage"
:statustext="major_tom_status_text"
:step="major_tom_step"
/>
<pull-progress
:progress="pull_output"
:show="show_pull_output"
:download="download_percentage"
:extraction="extraction_percentage"
:statustext="status_text"
:step="pull_step"
/>
<v-overlay
:z-index="10"
Expand Down Expand Up @@ -256,6 +265,10 @@ import {
LocalVersionsQuery, Version, VersionsQuery, VersionType,
} from '@/types/version-chooser'
import back_axios from '@/utils/api'
import {
installOrUpdateMajorTom,
resolveMajorTomAction,
} from '@/utils/major_tom'
import PullTracker from '@/utils/pull_tracker'
// Version Chooser Utils
import * as VCU from '@/utils/version_chooser'
Expand All @@ -281,6 +294,13 @@ export default Vue.extend({
bootstrap_version: undefined as (undefined | string),
pull_output: '',
show_pull_output: false,
pull_step: '',
show_major_tom_progress: false,
major_tom_pull_output: '',
major_tom_download_percentage: 0,
major_tom_extraction_percentage: 0,
major_tom_status_text: '',
major_tom_step: '',
page: 1,
local_versions: {
result: {
Expand Down Expand Up @@ -340,6 +360,69 @@ export default Vue.extend({
this.loadCurrentVersion()
},
methods: {
async runMajorTomInstallOrUpdate(): Promise<void> {
this.major_tom_status_text = 'Updating Major Tom...'
return new Promise<void>((resolve, reject) => {
const tracker = new PullTracker(
() => { resolve() },
(error) => { reject(new Error(error)) },
)

installOrUpdateMajorTom((progressEvent) => {
if (!progressEvent.event?.currentTarget?.response) {
return
}
tracker.digestNewData(progressEvent.event)
this.major_tom_pull_output = tracker.pull_output
this.major_tom_download_percentage = tracker.download_percentage
this.major_tom_extraction_percentage = tracker.extraction_percentage
this.major_tom_status_text = tracker.overall_status || 'Updating Major Tom...'
})
.then(() => { resolve() })
.catch(reject)
})
},
async updateMajorTomIfNeeded(): Promise<void> {
let step = ''
try {
const action = await resolveMajorTomAction(this.has_internet)
if (action === 'skip') {
return
}
step = action === 'install' ? 'Installing Major Tom' : 'Updating Major Tom'
} catch (error) {
console.warn('Failed to check for Major Tom updates:', error)
return
}

this.major_tom_step = step
this.major_tom_pull_output = ''
this.major_tom_download_percentage = 0
this.major_tom_extraction_percentage = 0
this.show_major_tom_progress = true
try {
await this.runMajorTomInstallOrUpdate()
} catch (error) {
notifier.pushError(
'MAJOR_TOM_UPDATE_FAIL',
`Failed to update Major Tom: ${error}`,
true,
)
} finally {
this.show_major_tom_progress = false
}
},
async applyVersion(fullname: string): Promise<void> {
const [repository, tag] = fullname.split(':')
await back_axios({
method: 'post',
url: '/version-chooser/v1.0/version/current',
data: {
repository,
tag,
},
}).finally(() => { this.waitForBackendToRestart(true) })
},
backendIsOnline() {
return new Promise((resolve) => {
back_axios({
Expand Down Expand Up @@ -533,13 +616,15 @@ export default Vue.extend({
},
async pullAndSetVersion(args: string | string[]) {
const fullname: string = Array.isArray(args) ? args[0] : args
await this.updateMajorTomIfNeeded()
// This streams the output of docker pull
this.pull_step = 'Updating BlueOS'
this.pull_output = 'Fetching remote image...'
this.show_pull_output = true
await this.pullVersion(fullname)
.then(() => {
this.show_pull_output = false
this.setVersion(fullname)
this.applyVersion(fullname)
})
.catch((error) => {
this.show_pull_output = false
Expand Down Expand Up @@ -596,6 +681,7 @@ export default Vue.extend({
async updateBootstrap(image: string) {
const [_, tag] = image.split(':')
this.updating_bootstrap = true
this.pull_step = 'Updating Bootstrap'
await this.pullVersion(image)
.then(() => {
this.show_pull_output = false
Expand Down Expand Up @@ -638,15 +724,8 @@ export default Vue.extend({
},
async setVersion(args: string | string[]) {
const fullname: string = Array.isArray(args) ? args[0] : args
const [repository, tag] = fullname.split(':')
await back_axios({
method: 'post',
url: '/version-chooser/v1.0/version/current',
data: {
repository,
tag,
},
}).finally(() => { this.waitForBackendToRestart(true) })
await this.updateMajorTomIfNeeded()
await this.applyVersion(fullname)
},
async deleteVersion(args: string | string[]) {
const fullname: string = Array.isArray(args) ? args[0] : args
Expand Down
65 changes: 65 additions & 0 deletions core/frontend/src/utils/major_tom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import axios from 'axios'

import { fetchInstalledExtensions } from '@/components/kraken/KrakenManager'
import { InstalledExtensionData } from '@/types/kraken'
import back_axios from '@/utils/api'

export const MAJOR_TOM_IDENTIFIER = 'blueos.major_tom'
export const MAJOR_TOM_INSTALL_URL = 'https://blueos.cloud/major_tom/install/'

const KRAKEN_API_V2_URL = '/kraken/v2.0'

export type MajorTomAction = 'skip' | 'install' | 'update'

export async function fetchMajorTomInstallData(): Promise<InstalledExtensionData> {
const response = await axios.get(MAJOR_TOM_INSTALL_URL)
return response.data as InstalledExtensionData
}

export async function getInstalledMajorTom(): Promise<InstalledExtensionData | undefined> {
const extensions = await fetchInstalledExtensions()
return extensions.find((extension) => extension.identifier === MAJOR_TOM_IDENTIFIER)
}

export async function resolveMajorTomAction(hasInternet: boolean): Promise<MajorTomAction> {
if (!hasInternet) {
return 'skip'
}

const [installed, latest] = await Promise.all([
getInstalledMajorTom(),
fetchMajorTomInstallData(),
])

if (!installed) {
return 'install'
}

if (installed.tag !== latest.tag) {
return 'update'
}

return 'skip'
}

export async function installOrUpdateMajorTom(
progressHandler: (event: { event: { currentTarget: { response: string } } }) => void,
): Promise<void> {
const majorTomData = await fetchMajorTomInstallData()

await back_axios({
method: 'POST',
url: `${KRAKEN_API_V2_URL}/extension/`,
data: {
identifier: majorTomData.identifier,
name: majorTomData.name,
docker: majorTomData.docker,
tag: majorTomData.tag,
enabled: true,
permissions: majorTomData?.permissions ?? '',
user_permissions: majorTomData?.user_permissions ?? '',
},
timeout: 600000,
onDownloadProgress: progressHandler,
})
}
9 changes: 3 additions & 6 deletions core/services/kraken/config.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# This file is used to define general configurations for the app

from typing import Dict, List

SERVICE_NAME = "kraken"

DEFAULT_MANIFESTS = [
Expand All @@ -10,12 +12,7 @@
},
]

DEFAULT_EXTENSIONS = [
{
"identifier": "blueos.major_tom",
"url": "https://blueos.cloud/major_tom/install",
},
]
DEFAULT_EXTENSIONS: List[Dict[str, str]] = []

DEFAULT_INJECTED_ENV_VARIABLES = [
"MAV_SYSTEM_ID",
Expand Down
Loading