fix(big fix upgrade): upgrade multiple areas of the core functionalities

This commit is contained in:
Philipp Kunz 2024-10-16 14:35:38 +02:00
parent d212dfb9f9
commit 53f96095c7
37 changed files with 2141 additions and 618 deletions

View File

@ -1,5 +1,12 @@
# Changelog # Changelog
## 2024-10-16 - 1.1.8 - fix(big fix upgrade)
fix: update dependency versions and address type errors
- Updated all listed dependencies in the package.json to their specified ranges.
- Fixed type mismatches and added missing imports in various TypeScript files.
- Refined existing tests and added a new helper to manage Docker image streams.
## 2024-08-25 - 1.1.7 - fix(deps) ## 2024-08-25 - 1.1.7 - fix(deps)
Update dependencies to latest versions Update dependencies to latest versions

View File

@ -25,20 +25,20 @@
"@git.zone/tsbundle": "^2.0.15", "@git.zone/tsbundle": "^2.0.15",
"@git.zone/tstest": "^1.0.90", "@git.zone/tstest": "^1.0.90",
"@git.zone/tswatch": "^2.0.23", "@git.zone/tswatch": "^2.0.23",
"@push.rocks/tapbundle": "^5.0.24", "@push.rocks/tapbundle": "^5.3.0",
"@types/node": "^22.5.0" "@types/node": "^22.7.5"
}, },
"dependencies": { "dependencies": {
"@api.global/typedrequest": "3.0.30", "@api.global/typedrequest": "3.1.10",
"@api.global/typedserver": "^3.0.50", "@api.global/typedserver": "^3.0.51",
"@api.global/typedsocket": "^3.0.1", "@api.global/typedsocket": "^3.0.1",
"@apiclient.xyz/cloudflare": "^6.0.1", "@apiclient.xyz/cloudflare": "^6.0.1",
"@apiclient.xyz/docker": "^1.2.3", "@apiclient.xyz/docker": "^1.2.7",
"@apiclient.xyz/hetznercloud": "^1.2.0", "@apiclient.xyz/hetznercloud": "^1.2.0",
"@apiclient.xyz/slack": "^3.0.9", "@apiclient.xyz/slack": "^3.0.9",
"@design.estate/dees-catalog": "^1.1.6", "@design.estate/dees-catalog": "^1.2.0",
"@design.estate/dees-domtools": "^2.0.57", "@design.estate/dees-domtools": "^2.0.64",
"@design.estate/dees-element": "^2.0.36", "@design.estate/dees-element": "^2.0.39",
"@git.zone/tsrun": "^1.2.49", "@git.zone/tsrun": "^1.2.49",
"@push.rocks/early": "^4.0.3", "@push.rocks/early": "^4.0.3",
"@push.rocks/npmextra": "^5.0.23", "@push.rocks/npmextra": "^5.0.23",
@ -48,14 +48,14 @@
"@push.rocks/smartbucket": "^3.0.22", "@push.rocks/smartbucket": "^3.0.22",
"@push.rocks/smartcli": "^4.0.11", "@push.rocks/smartcli": "^4.0.11",
"@push.rocks/smartclickhouse": "^2.0.17", "@push.rocks/smartclickhouse": "^2.0.17",
"@push.rocks/smartdata": "^5.2.6", "@push.rocks/smartdata": "^5.2.10",
"@push.rocks/smartdelay": "^3.0.5", "@push.rocks/smartdelay": "^3.0.5",
"@push.rocks/smartexit": "^1.0.23", "@push.rocks/smartexit": "^1.0.23",
"@push.rocks/smartexpect": "^1.2.1", "@push.rocks/smartexpect": "^1.2.1",
"@push.rocks/smartfile": "^11.0.21", "@push.rocks/smartfile": "^11.0.21",
"@push.rocks/smartguard": "^3.0.2", "@push.rocks/smartguard": "^3.1.0",
"@push.rocks/smartjson": "^5.0.19", "@push.rocks/smartjson": "^5.0.19",
"@push.rocks/smartjwt": "^2.0.4", "@push.rocks/smartjwt": "^2.2.1",
"@push.rocks/smartlog": "^3.0.7", "@push.rocks/smartlog": "^3.0.7",
"@push.rocks/smartlog-destination-clickhouse": "^1.0.13", "@push.rocks/smartlog-destination-clickhouse": "^1.0.13",
"@push.rocks/smartpath": "^5.0.18", "@push.rocks/smartpath": "^5.0.18",
@ -63,13 +63,13 @@
"@push.rocks/smartrequest": "^2.0.22", "@push.rocks/smartrequest": "^2.0.22",
"@push.rocks/smartrx": "^3.0.7", "@push.rocks/smartrx": "^3.0.7",
"@push.rocks/smartssh": "^2.0.1", "@push.rocks/smartssh": "^2.0.1",
"@push.rocks/smartstate": "^2.0.17", "@push.rocks/smartstate": "^2.0.19",
"@push.rocks/smartstream": "^3.0.44", "@push.rocks/smartstream": "^3.2.4",
"@push.rocks/smartstring": "^4.0.15", "@push.rocks/smartstring": "^4.0.15",
"@push.rocks/smartunique": "^3.0.9", "@push.rocks/smartunique": "^3.0.9",
"@push.rocks/taskbuffer": "^3.0.2", "@push.rocks/taskbuffer": "^3.0.2",
"@push.rocks/webjwt": "^1.0.9", "@push.rocks/webjwt": "^1.0.9",
"@serve.zone/interfaces": "^1.0.78", "@serve.zone/interfaces": "^1.1.2",
"@tsclass/tsclass": "^4.1.2" "@tsclass/tsclass": "^4.1.2"
}, },
"files": [ "files": [

2265
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -21,3 +21,7 @@ export const createCloudly = async () => {
return cloudlyInstance; return cloudlyInstance;
} }
export const getEnvVarOnDemand = async (envVarName: string) => {
return testQenv.getEnvVarOnDemand(envVarName);
}

9
test/helpers/docker.ts Normal file
View File

@ -0,0 +1,9 @@
import * as smartstream from '@push.rocks/smartstream';
import * as smartpath from '@push.rocks/smartpath';
export const getAlpineImageReadableStream = async () => {
const currentDir = smartpath.get.dirnameFromImportMetaUrl(import.meta.url);
const imagePath = smartpath.join(currentDir, '../../.nogit/testfiles/alpine.tar');
const readableStream = smartstream.nodewebhelpers.createWebReadableStreamFromFile(imagePath);
return readableStream;
}

View File

@ -1 +1,2 @@
export * from './cloudlyfactory.js'; export * from './cloudlyfactory.js';
export * from './docker.js';

View File

@ -1,30 +1,80 @@
import { tap, expect } from '@push.rocks/tapbundle'; import { tap, expect } from '@push.rocks/tapbundle';
import * as helpers from './helpers/index.js';
import * as cloudly from '../ts/index.js';
import * as cloudlyApiClient from '../ts_apiclient/index.js'; import * as cloudlyApiClient from '../ts_apiclient/index.js';
import { Image } from '../ts_apiclient/classes.image.js';
let testCloudly: cloudly.Cloudly;
let testClient: cloudlyApiClient.CloudlyApiClient; let testClient: cloudlyApiClient.CloudlyApiClient;
tap.preTask('should start cloudly', async () => {
testCloudly = await helpers.createCloudly();
await testCloudly.start();
});
tap.preTask('should create a new machine user for testing', async () => {
const machineUser = new testCloudly.authManager.CUser();
machineUser.id = await testCloudly.authManager.CUser.getNewId();
machineUser.data = {
type: 'machine',
username: 'test',
password: 'test',
tokens: [{
token: 'test',
expiresAt: Date.now() + 3600 * 1000 * 24 * 365,
assignedRoles: ['admin'],
}],
role: 'admin',
};
await machineUser.save();
});
tap.test('should create a new cloudlyApiClient', async () => { tap.test('should create a new cloudlyApiClient', async () => {
testClient = new cloudlyApiClient.CloudlyApiClient({ testClient = new cloudlyApiClient.CloudlyApiClient({
registerAs: 'api', registerAs: 'api',
cloudlyUrl: 'http://localhost:3000', cloudlyUrl: `http://localhost:${await helpers.getEnvVarOnDemand('SERVEZONE_PORT')}`,
}); });
// await testClient.start(); await testClient.start();
expect(testClient).toBeTruthy(); expect(testClient).toBeTruthy();
}); });
tap.test('should get an identity', async () => { tap.test('create a new machine user', async () => {
const identity = await testClient.getIdentityByJumpCode('test'); const machineUser = await testCloudly.authManager.CUser.createMachineUser('test', 'api');
expect(identity).toBeTruthy(); machineUser.data.tokens.push({
}); token: 'test',
expiresAt: Date.now() + 3600 * 1000 * 24 * 365,
tap.test('should trigger a server action', async () => { assignedRoles: ['api'],
})
await machineUser.save();
}) })
tap.test('should get an identity', async () => {
const identity = await testClient.getIdentityByToken('test');
expect(identity).toBeTruthy();
console.log(identity);
});
let image: Image;
tap.test('should create and upload an image', async () => {
image = await testClient.images.createImage({
name: 'test',
description: 'test'
});
console.log('created image: ', image);
expect(image).toBeInstanceOf(Image);
})
tap.test('should upload an image version', async () => {
const imageStream = await helpers.getAlpineImageReadableStream();
await image.pushImageVersion('v1.0.0', imageStream);
});
tap.test('should stop the apiclient', async (toolsArg) => { tap.test('should stop the apiclient', async (toolsArg) => {
await toolsArg.delayFor(1000); await toolsArg.delayFor(10000);
await testClient.stop(); await testClient.stop();
await testCloudly.stop();
}) })
export default tap.start(); export default tap.start();

View File

@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@serve.zone/cloudly', name: '@serve.zone/cloudly',
version: '1.1.7', version: '1.1.8',
description: 'A comprehensive multi-cloud manager leveraging Docker Swarmkit to orchestrate containerized applications across various cloud services and provide robust configuration and API integration.' description: 'A comprehensive multi-cloud manager leveraging Docker Swarmkit to orchestrate containerized applications across various cloud services and provide robust configuration and API integration.'
} }

View File

@ -28,6 +28,10 @@ export class CloudlyAuthManager {
this.cloudlyRef.typedrouter.addTypedRouter(this.typedrouter); this.cloudlyRef.typedrouter.addTypedRouter(this.typedrouter);
} }
public async createNewSecureToken() {
return plugins.smartunique.uniSimple('secureToken', 64);
}
public async start() { public async start() {
// lets setup the smartjwtInstance // lets setup the smartjwtInstance
this.smartjwtInstance = new plugins.smartjwt.SmartJwt(); this.smartjwtInstance = new plugins.smartjwt.SmartJwt();
@ -53,6 +57,7 @@ export class CloudlyAuthManager {
const user = await User.findUserByUsernameAndPassword(dataArg.username, dataArg.password); const user = await User.findUserByUsernameAndPassword(dataArg.username, dataArg.password);
if (!user) { if (!user) {
logger.log('warn', 'login failed'); logger.log('warn', 'login failed');
throw new plugins.typedrequest.TypedResponseError('login failed');
} else { } else {
jwt = await this.smartjwtInstance.createJWT({ jwt = await this.smartjwtInstance.createJWT({
userId: user.id, userId: user.id,
@ -96,7 +101,7 @@ export class CloudlyAuthManager {
}); });
public adminIdentityGuard = new plugins.smartguard.Guard<{identity: plugins.servezoneInterfaces.data.IIdentity}>(async (dataArg) => { public adminIdentityGuard = new plugins.smartguard.Guard<{identity: plugins.servezoneInterfaces.data.IIdentity}>(async (dataArg) => {
await plugins.smartexpect.expectAsync(this.validIdentityGuard.exec(dataArg)).toBeTrue(); await plugins.smartguard.passGuardsOrReject(dataArg, [this.validIdentityGuard]);
const jwt = dataArg.identity.jwt; const jwt = dataArg.identity.jwt;
const jwtData: IJwtData = await this.smartjwtInstance.verifyJWTAndGetData(jwt); const jwtData: IJwtData = await this.smartjwtInstance.verifyJWTAndGetData(jwt);
const user = await this.CUser.getInstance({id: jwtData.userId}); const user = await this.CUser.getInstance({id: jwtData.userId});

View File

@ -5,6 +5,26 @@ export class User extends plugins.smartdata.SmartDataDbDoc<
User, User,
plugins.servezoneInterfaces.data.IUser plugins.servezoneInterfaces.data.IUser
> { > {
/**
* creates a machine user
*/
public static async createMachineUser(userNameArg: string, roleArg: 'api' | 'cluster') {
const user = new User();
user.id = await User.getNewId();
user.data = {
type: 'machine',
username: userNameArg,
tokens: [{
token: 'machineUser',
expiresAt: Date.now() + 3600 * 1000 * 24 * 365,
assignedRoles: ['admin'],
}],
role: 'api',
};
await user.save();
return user;
}
public static async findUserByUsernameAndPassword(usernameArg: string, passwordArg: string) { public static async findUserByUsernameAndPassword(usernameArg: string, passwordArg: string) {
return await User.getInstance({ return await User.getInstance({
data: { data: {
@ -14,6 +34,13 @@ export class User extends plugins.smartdata.SmartDataDbDoc<
}); });
} }
constructor(optionsArg?: plugins.servezoneInterfaces.data.IUser) {
super();
if (optionsArg) {
Object.assign(this, optionsArg);
}
}
// INSTANCE // INSTANCE
@plugins.smartdata.unI() @plugins.smartdata.unI()
public id: string; public id: string;

View File

@ -4,6 +4,7 @@ import { Cloudly } from '../classes.cloudly.js';
import { logger } from '../logger.js'; import { logger } from '../logger.js';
import { Cluster } from './classes.cluster.js'; import { Cluster } from './classes.cluster.js';
import { data } from '@serve.zone/interfaces';
export class ClusterManager { export class ClusterManager {
public ready = plugins.smartpromise.defer(); public ready = plugins.smartpromise.defer();
@ -26,10 +27,8 @@ export class ClusterManager {
const cluster = await this.createCluster({ const cluster = await this.createCluster({
id: plugins.smartunique.uniSimple('cluster'), id: plugins.smartunique.uniSimple('cluster'),
data: { data: {
userId: null, userId: null, // this is created by the createCluster method
name: dataArg.clusterName, name: dataArg.clusterName,
initialJumpToken: plugins.smartunique.uniSimple('initialJumpToken'),
initialJumpTokenUsedAt: null,
acmeInfo: null, acmeInfo: null,
cloudlyUrl: `https://${this.cloudlyRef.config.data.publicUrl}:${this.cloudlyRef.config.data.publicPort}/`, cloudlyUrl: `https://${this.cloudlyRef.config.data.publicUrl}:${this.cloudlyRef.config.data.publicPort}/`,
servers: [], servers: [],
@ -82,19 +81,17 @@ export class ClusterManager {
// TODO: implement getclusterConfigByServerIp // TODO: implement getclusterConfigByServerIp
} }
public async getClusterBy_JumpCode(initialJumpTokenArg: string) { public async getClusterBy_UserId(userIdArg: string) {
await this.ready.promise; await this.ready.promise;
return await Cluster.getInstance({ return await Cluster.getInstance({
data: { data: {
initialJumpToken: initialJumpTokenArg, userId: userIdArg,
}, },
}); });
} }
public async getClusterBy_Identity( public async getClusterBy_Identity(clusterIdentity: plugins.servezoneInterfaces.data.IIdentity) {
clusterIdentity: plugins.servezoneInterfaces.data.IIdentity
) {
await this.ready.promise; await this.ready.promise;
return await Cluster.getInstance({ return await Cluster.getInstance({
@ -125,19 +122,27 @@ export class ClusterManager {
} }
/** /**
* allows storage of a config * creates a cluster (and a new user for it) and saves it
* @param configName * @param configName
* @param configObjectArg * @param configObjectArg
*/ */
public async createCluster(configObjectArg: plugins.servezoneInterfaces.data.ICluster) { public async createCluster(configObjectArg: plugins.servezoneInterfaces.data.ICluster) {
// TODO: guards // TODO: guards
// lets create the cluster user // lets create the cluster user
const clusterUser = new this.cloudlyRef.authManager.CUser(); const clusterUser = new this.cloudlyRef.authManager.CUser({
clusterUser.id = await this.cloudlyRef.authManager.CUser.getNewId(); id: await this.cloudlyRef.authManager.CUser.getNewId(),
clusterUser.data = { data: {
role: 'cluster', role: 'cluster',
type: 'machine', type: 'machine',
} tokens: [
{
expiresAt: Date.now() + 3600 * 1000 * 24 * 365,
assignedRoles: ['cluster'],
token: await this.cloudlyRef.authManager.createNewSecureToken(),
},
],
},
});
await clusterUser.save(); await clusterUser.save();
Object.assign(configObjectArg, { Object.assign(configObjectArg, {
userId: clusterUser.id, userId: clusterUser.id,

View File

@ -1,5 +1,6 @@
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import { Cloudly } from '../classes.cloudly.js'; import { Cloudly } from '../classes.cloudly.js';
import type { Cluster } from '../manager.cluster/classes.cluster.js';
/** /**
* in charge of talking to coreflow services on clusters * in charge of talking to coreflow services on clusters
@ -13,38 +14,41 @@ export class CloudlyCoreflowManager {
this.cloudlyRef = cloudlyRefArg; this.cloudlyRef = cloudlyRefArg;
this.cloudlyRef.typedrouter.addTypedRouter(this.typedRouter); this.cloudlyRef.typedrouter.addTypedRouter(this.typedRouter);
this.typedRouter.addTypedHandler<plugins.servezoneInterfaces.requests.identity.IRequest_Any_Cloudly_CoreflowManager_GetIdentityByJumpCode>( this.typedRouter.addTypedHandler<plugins.servezoneInterfaces.requests.identity.IRequest_Any_Cloudly_CoreflowManager_GetIdentityByToken>(
new plugins.typedrequest.TypedHandler('getIdentityByJumpCode', async (requestData) => { new plugins.typedrequest.TypedHandler('getIdentityByToken', async (requestData) => {
const cluster =
await this.cloudlyRef.clusterManager.getClusterBy_JumpCode(
requestData.jumpCode
);
if (!cluster) {
throw new plugins.typedrequest.TypedResponseError('The supplied jumpCode is not valid. No cluster found.');
}
const user = await this.cloudlyRef.authManager.CUser.getInstance({ const user = await this.cloudlyRef.authManager.CUser.getInstance({
id: cluster.data.userId, data: {
tokens: [{
token: requestData.token,
}] // find the proper user here.
} as any
}); });
if (!user) { if (!user) {
throw new plugins.typedrequest.TypedResponseError('The supplied jumpCode is not valid. No user found.'); throw new plugins.typedrequest.TypedResponseError('The supplied token is not valid. No matching user found.');
}
if (user.data.type !== 'machine') {
throw new plugins.typedrequest.TypedResponseError('The supplied token is not valid. The user is not a machine.');
}
let cluster: Cluster;
if (user.data.role === 'cluster') {
cluster = await this.cloudlyRef.clusterManager.getClusterBy_UserId(user.id);
} }
const expiryTimestamp = Date.now() + 3600 * 1000 * 24 * 365; const expiryTimestamp = Date.now() + 3600 * 1000 * 24 * 365;
return { return {
identity: { identity: {
name: cluster.data.name, name: user.data.username,
role: 'cluster', role: user.data.role,
type: 'machine', type: 'machine', // if someone authenticates by token, they are a machine, no matter what.
userId: cluster.data.userId, userId: user.id,
expiresAt: expiryTimestamp, expiresAt: expiryTimestamp,
clusterId: cluster.id, ...(cluster ? {
clusterName: cluster.data.name, clusterId: cluster.id,
clusterName: cluster.data.name,
} : {}),
jwt: await this.cloudlyRef.authManager.smartjwtInstance.createJWT({ jwt: await this.cloudlyRef.authManager.smartjwtInstance.createJWT({
status: 'loggedIn', status: 'loggedIn',
userId: cluster.data.userId, userId: user.id,
expiresAt: expiryTimestamp, expiresAt: expiryTimestamp,
}) })
}, },

View File

@ -34,4 +34,12 @@ export class Image extends plugins.smartdata.SmartDataDbDoc<Image, plugins.serve
public async getStoragePath(versionStringArg: string) { public async getStoragePath(versionStringArg: string) {
return `${this.data.name}:${versionStringArg}`.replace('/', '__') return `${this.data.name}:${versionStringArg}`.replace('/', '__')
} }
public async getWriteStream() {
}
public async getReadStream() {
}
} }

View File

@ -1,5 +1,6 @@
import type { Cloudly } from '../classes.cloudly.js'; import type { Cloudly } from '../classes.cloudly.js';
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import * as paths from '../paths.js';
import { Image } from './classes.image.js'; import { Image } from './classes.image.js';
@ -9,14 +10,13 @@ export class ImageManager {
public smartbucketInstance: plugins.smartbucket.SmartBucket; public smartbucketInstance: plugins.smartbucket.SmartBucket;
public imageDir: plugins.smartbucket.Directory; public imageDir: plugins.smartbucket.Directory;
public dockerImageStore: plugins.docker.DockerImageStore; public dockerImageStore: plugins.docker.DockerImageStore;
get db() { get db() {
return this.cloudlyRef.mongodbConnector.smartdataDb; return this.cloudlyRef.mongodbConnector.smartdataDb;
} }
public CImage = plugins.smartdata.setDefaultManagerForDoc(this, Image); public CImage = plugins.smartdata.setDefaultManagerForDoc(this, Image);
constructor(cloudlyRefArg: Cloudly) { constructor(cloudlyRefArg: Cloudly) {
this.cloudlyRef = cloudlyRefArg; this.cloudlyRef = cloudlyRefArg;
@ -37,7 +37,19 @@ export class ImageManager {
}; };
} }
) )
) );
this.typedrouter.addTypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_GetImage>(
new plugins.typedrequest.TypedHandler('getImage', async (reqArg, toolsArg) => {
await toolsArg.passGuards([this.cloudlyRef.authManager.adminIdentityGuard], reqArg);
const image = await this.CImage.getInstance({
id: reqArg.imageId,
});
return {
image: await image.createSavableObject(),
};
})
);
this.typedrouter.addTypedHandler( this.typedrouter.addTypedHandler(
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_DeleteImage>( new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_DeleteImage>(
@ -74,14 +86,33 @@ export class ImageManager {
new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_PushImageVersion>( new plugins.typedrequest.TypedHandler<plugins.servezoneInterfaces.requests.image.IRequest_PushImageVersion>(
'pushImageVersion', 'pushImageVersion',
async (reqArg, toolsArg) => { async (reqArg, toolsArg) => {
const image = await this.CImage.getInstance({ await plugins.smartguard.passGuardsOrReject(reqArg, [
this.cloudlyRef.authManager.validIdentityGuard,
]);
const refImage = await this.CImage.getInstance({
id: reqArg.imageId, id: reqArg.imageId,
}); });
if (!image) { if (!refImage) {
throw new plugins.typedrequest.TypedResponseError('Image not found'); throw new plugins.typedrequest.TypedResponseError('Image not found');
} }
const imageVersion = reqArg.versionString; const imageVersion = reqArg.versionString;
console.log(
`got request to push image version ${imageVersion} for image ${refImage.data.name}`
);
const imagePushStream = reqArg.imageStream; const imagePushStream = reqArg.imageStream;
(async () => {
const smartWebDuplex = new plugins.smartstream.webstream.WebDuplexStream<
Uint8Array,
Uint8Array
>({
writeFunction: async (chunkArg, toolsArg) => {
console.log(chunkArg);
return chunkArg;
},
});
imagePushStream.writeToWebstream(smartWebDuplex.writable);
await this.dockerImageStore.storeImage(refImage.id, plugins.smartstream.SmartDuplex.fromWebReadableStream(smartWebDuplex.readable));
})();
return { return {
allowed: true, allowed: true,
}; };
@ -94,12 +125,17 @@ export class ImageManager {
'pullImageVersion', 'pullImageVersion',
async (reqArg) => { async (reqArg) => {
const image = await this.CImage.getInstance({ const image = await this.CImage.getInstance({
id: reqArg.imageId, id: reqArg.imageId,
}); });
const imageVersion = image.data.versions.find((version) => version.versionString === reqArg.versionString); const imageVersion = image.data.versions.find(
const readable = this.imageDir.fastGetStream({ (version) => version.versionString === reqArg.versionString
path: await image.getStoragePath(reqArg.versionString), );
}, 'webstream'); const readable = this.imageDir.fastGetStream(
{
path: await image.getStoragePath(reqArg.versionString),
},
'webstream'
);
const imageVirtualStream = new plugins.typedrequest.VirtualStream(); const imageVirtualStream = new plugins.typedrequest.VirtualStream();
return { return {
imageStream: imageVirtualStream, imageStream: imageVirtualStream,
@ -110,6 +146,7 @@ export class ImageManager {
} }
public async start() { public async start() {
// lets setup s3
const s3Descriptor: plugins.tsclass.storage.IS3Descriptor = const s3Descriptor: plugins.tsclass.storage.IS3Descriptor =
await this.cloudlyRef.config.appData.waitForAndGetKey('s3Descriptor'); await this.cloudlyRef.config.appData.waitForAndGetKey('s3Descriptor');
console.log(this.cloudlyRef.config.data.s3Descriptor); console.log(this.cloudlyRef.config.data.s3Descriptor);
@ -117,10 +154,17 @@ export class ImageManager {
this.cloudlyRef.config.data.s3Descriptor this.cloudlyRef.config.data.s3Descriptor
); );
const bucket = await this.smartbucketInstance.getBucketByName('cloudly-test'); const bucket = await this.smartbucketInstance.getBucketByName('cloudly-test');
await bucket.fastPut({ path: 'test/test.txt', contents: 'hello' }); await bucket.fastPut({ path: 'images/00init', contents: 'init' });
this.imageDir = await bucket.getDirectoryFromPath({ this.imageDir = await bucket.getDirectoryFromPath({
path: 'images', path: '/images',
});
// lets setup dockerstore
await plugins.smartfile.fs.ensureDir(paths.dockerImageStoreDir);
this.dockerImageStore = new plugins.docker.DockerImageStore({
localDirPath: paths.dockerImageStoreDir,
bucketDir: this.imageDir,
}); });
} }
} }

View File

@ -2,4 +2,5 @@ import * as plugins from './plugins.js';
export const packageDir = plugins.path.join(plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url), '../'); export const packageDir = plugins.path.join(plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url), '../');
export const nogitDir = plugins.path.join(packageDir, '.nogit/'); export const nogitDir = plugins.path.join(packageDir, '.nogit/');
export const dockerImageStoreDir = plugins.path.join(nogitDir, './dockerimagestore/');
export const distServeDir = plugins.path.join(packageDir, './dist_serve'); export const distServeDir = plugins.path.join(packageDir, './dist_serve');

View File

@ -43,6 +43,7 @@ import * as smartpath from '@push.rocks/smartpath';
import * as smartpromise from '@push.rocks/smartpromise'; import * as smartpromise from '@push.rocks/smartpromise';
import * as smartrequest from '@push.rocks/smartrequest'; import * as smartrequest from '@push.rocks/smartrequest';
import * as smartssh from '@push.rocks/smartssh'; import * as smartssh from '@push.rocks/smartssh';
import * as smartstream from '@push.rocks/smartstream';
import * as smartstring from '@push.rocks/smartstring'; import * as smartstring from '@push.rocks/smartstring';
import * as smartunique from '@push.rocks/smartunique'; import * as smartunique from '@push.rocks/smartunique';
import * as taskbuffer from '@push.rocks/taskbuffer'; import * as taskbuffer from '@push.rocks/taskbuffer';
@ -69,6 +70,7 @@ export {
smartpromise, smartpromise,
smartrequest, smartrequest,
smartssh, smartssh,
smartstream,
smartstring, smartstring,
smartunique, smartunique,
taskbuffer, taskbuffer,

View File

@ -55,7 +55,7 @@ export class CloudlyApiClient {
this.cloudlyUrl this.cloudlyUrl
); );
console.log( console.log(
`CloudlyCluent connected to cloudly at ${this.cloudlyUrl}. Remember to get an identity.` `CloudlyClient connected to cloudly at ${this.cloudlyUrl}. Remember to get an identity.`
); );
} }
@ -64,8 +64,8 @@ export class CloudlyApiClient {
} }
public identity: plugins.servezoneInterfaces.data.IIdentity; public identity: plugins.servezoneInterfaces.data.IIdentity;
public async getIdentityByJumpCode( public async getIdentityByToken(
jumpCodeArg: string, token: string,
optionsArg?: { optionsArg?: {
tagConnection?: boolean; tagConnection?: boolean;
statefullIdentity?: boolean; statefullIdentity?: boolean;
@ -77,12 +77,12 @@ export class CloudlyApiClient {
}, optionsArg); }, optionsArg);
const identityRequest = const identityRequest =
this.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.identity.IRequest_Any_Cloudly_CoreflowManager_GetIdentityByJumpCode>( this.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.identity.IRequest_Any_Cloudly_CoreflowManager_GetIdentityByToken>(
'getIdentityByJumpCode' 'getIdentityByToken'
); );
console.log(`trying to get identity from cloudly with supplied jumpCodeArg: ${jumpCodeArg}`); console.log(`trying to get identity from cloudly with supplied jumpCodeArg: ${token}`);
const response = await identityRequest.fire({ const response = await identityRequest.fire({
jumpCode: jumpCodeArg, token: token,
}); });
console.log('got identity response'); console.log('got identity response');
const identity = response.identity; const identity = response.identity;
@ -155,8 +155,13 @@ export class CloudlyApiClient {
return typedResponse.certificate; return typedResponse.certificate;
} }
// Images public images = {
public async getImages() { // Images
return Image.getImages(this); getImages: async () => {
return Image.getImages(this);
},
createImage: async (optionsArg: Parameters<typeof Image.createImage>[1]) => {
return Image.createImage(this, optionsArg);
}
} }
} }

View File

@ -49,8 +49,8 @@ export class Image implements plugins.servezoneInterfaces.data.IImage {
* updates the image data * updates the image data
*/ */
public async update() { public async update() {
const getVersionsTR = this.cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.image.IRequest_GetImageMetadata>( const getVersionsTR = this.cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.image.IRequest_GetImage>(
'getImageMetadata' 'getImage'
); );
const response = await getVersionsTR.fire({ const response = await getVersionsTR.fire({
identity: this.cloudlyClientRef.identity, identity: this.cloudlyClientRef.identity,
@ -66,18 +66,17 @@ export class Image implements plugins.servezoneInterfaces.data.IImage {
*/ */
public async pushImageVersion(imageVersion: string, imageReadableArg: ReadableStream<Uint8Array>): Promise<void> { public async pushImageVersion(imageVersion: string, imageReadableArg: ReadableStream<Uint8Array>): Promise<void> {
const done = plugins.smartpromise.defer(); const done = plugins.smartpromise.defer();
const pullImageTR = this.cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.image.IRequest_PushImageVersion>( const pushImageTR = this.cloudlyClientRef.typedsocketClient.createTypedRequest<plugins.servezoneInterfaces.requests.image.IRequest_PushImageVersion>(
'pushImageVersion' 'pushImageVersion'
); );
const virtualStream = new plugins.typedrequest.VirtualStream(); const virtualStream = new plugins.typedrequest.VirtualStream();
const response = await pullImageTR.fire({ const response = await pushImageTR.fire({
identity: this.cloudlyClientRef.identity, identity: this.cloudlyClientRef.identity,
imageId: this.id, imageId: this.id,
versionString: '', versionString: '',
imageStream: virtualStream, imageStream: virtualStream,
}); });
await virtualStream.readFromWebstream(imageReadableArg); await virtualStream.readFromWebstream(imageReadableArg);
await done.promise;
await this.update(); await this.update();
}; };

View File

@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@serve.zone/cloudly', name: '@serve.zone/cloudly',
version: '1.1.7', version: '1.1.8',
description: 'A comprehensive multi-cloud manager leveraging Docker Swarmkit to orchestrate containerized applications across various cloud services and provide robust configuration and API integration.' description: 'A comprehensive multi-cloud manager leveraging Docker Swarmkit to orchestrate containerized applications across various cloud services and provide robust configuration and API integration.'
} }

View File

@ -22,6 +22,11 @@ export const loginAction = loginStatePart.createAction<{ username: string; passw
const response = await trLogin.fire({ const response = await trLogin.fire({
username: payloadArg.username, username: payloadArg.username,
password: payloadArg.password, password: payloadArg.password,
}).catch(err => {
console.log(err);
return {
...statePartArg.getState(),
}
}); });
return { return {
...currentState, ...currentState,

View File

@ -97,6 +97,10 @@ export class CloudlyDashboard extends DeesElement {
name: 'Services', name: 'Services',
element: CloudlyViewServices, element: CloudlyViewServices,
}, },
{
name: 'Testing & Building',
element: CloudlyViewServices,
},
{ {
name: 'Deployments', name: 'Deployments',
element: CloudlyViewDeployments, element: CloudlyViewDeployments,

View File

@ -1,4 +1,5 @@
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import * as shared from '../elements/shared/index.js';
import { import {
DeesElement, DeesElement,
@ -31,13 +32,9 @@ export class CloudlyViewBackups extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
shared.viewHostCss,
css` css`
:host {
display: block;
margin: auto;
max-width: 1280px;
padding: 16px 16px;
}
`, `,
]; ];

View File

@ -1,4 +1,5 @@
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import * as shared from '../elements/shared/index.js';
import { import {
DeesElement, DeesElement,
@ -31,13 +32,9 @@ export class CloudlyViewClusters extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
shared.viewHostCss,
css` css`
:host {
display: block;
margin: auto;
max-width: 1280px;
padding: 16px 16px;
}
`, `,
]; ];

View File

@ -1,4 +1,5 @@
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import * as shared from '../elements/shared/index.js';
import { import {
DeesElement, DeesElement,
@ -31,13 +32,9 @@ export class CloudlyViewDbs extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
shared.viewHostCss,
css` css`
:host {
display: block;
margin: auto;
max-width: 1280px;
padding: 16px 16px;
}
`, `,
]; ];

View File

@ -1,4 +1,5 @@
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import * as shared from '../elements/shared/index.js';
import { import {
DeesElement, DeesElement,
@ -31,13 +32,9 @@ export class CloudlyViewDeployments extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
shared.viewHostCss,
css` css`
:host {
display: block;
margin: auto;
max-width: 1280px;
padding: 16px 16px;
}
`, `,
]; ];

View File

@ -1,4 +1,5 @@
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import * as shared from '../elements/shared/index.js';
import { import {
DeesElement, DeesElement,
@ -31,13 +32,8 @@ export class CloudlyViewDns extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
css` shared.viewHostCss,
:host { css`
display: block;
margin: auto;
max-width: 1280px;
padding: 16px 16px;
}
`, `,
]; ];

View File

@ -1,4 +1,5 @@
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import * as shared from '../elements/shared/index.js';
import { DeesElement, customElement, html, state, css, cssManager } from '@design.estate/dees-element'; import { DeesElement, customElement, html, state, css, cssManager } from '@design.estate/dees-element';
@ -23,13 +24,9 @@ export class CloudlyViewImages extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
shared.viewHostCss,
css` css`
:host {
display: block;
margin: auto;
max-width: 1280px;
padding: 16px 16px;
}
`, `,
]; ];

View File

@ -1,4 +1,5 @@
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import * as shared from '../elements/shared/index.js';
import { import {
DeesElement, DeesElement,
@ -31,13 +32,9 @@ export class CloudlyViewLogs extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
shared.viewHostCss,
css` css`
:host {
display: block;
margin: auto;
max-width: 1280px;
padding: 16px 16px;
}
`, `,
]; ];

View File

@ -1,4 +1,5 @@
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import * as shared from '../elements/shared/index.js';
import { import {
DeesElement, DeesElement,
@ -31,14 +32,8 @@ export class CloudlyViewMails extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
css` shared.viewHostCss,
:host { css``
display: block;
margin: auto;
max-width: 1280px;
padding: 16px 16px;
}
`,
]; ];
public render() { public render() {

View File

@ -1,4 +1,5 @@
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import * as shared from '../elements/shared/index.js';
import { import {
DeesElement, DeesElement,
@ -31,13 +32,8 @@ export class CloudlyViewOverview extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
shared.viewHostCss,
css` css`
:host {
display: block;
margin: auto;
max-width: 1280px;
padding: 8px 16px;
}
.clusterGrid { .clusterGrid {
display: grid; display: grid;
grid-template-columns: ${cssManager.cssGridColumns(3, 8)}; grid-template-columns: ${cssManager.cssGridColumns(3, 8)};

View File

@ -1,4 +1,5 @@
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import * as shared from '../elements/shared/index.js';
import { import {
DeesElement, DeesElement,
@ -31,13 +32,9 @@ export class CloudlyViewS3 extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
shared.viewHostCss,
css` css`
:host {
display: block;
margin: auto;
max-width: 1280px;
padding: 16px 16px;
}
`, `,
]; ];

View File

@ -1,4 +1,5 @@
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import * as shared from '../elements/shared/index.js';
import { import {
DeesElement, DeesElement,
@ -31,13 +32,9 @@ export class CloudlyViewSecretBundles extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
shared.viewHostCss,
css` css`
:host {
display: block;
margin: auto;
max-width: 1280px;
padding: 16px 16px;
}
`, `,
]; ];

View File

@ -1,4 +1,5 @@
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import * as shared from '../elements/shared/index.js';
import { DeesElement, customElement, html, state, css, cssManager } from '@design.estate/dees-element'; import { DeesElement, customElement, html, state, css, cssManager } from '@design.estate/dees-element';
@ -23,13 +24,9 @@ export class CloudlyViewSecretGroups extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
shared.viewHostCss,
css` css`
:host {
display: block;
margin: auto;
max-width: 1280px;
padding: 16px 16px;
}
`, `,
]; ];

View File

@ -1,4 +1,5 @@
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import * as shared from '../elements/shared/index.js';
import { import {
DeesElement, DeesElement,
@ -31,13 +32,9 @@ export class CloudlyViewServices extends DeesElement {
public static styles = [ public static styles = [
cssManager.defaultStyles, cssManager.defaultStyles,
shared.viewHostCss,
css` css`
:host {
display: block;
margin: auto;
max-width: 1280px;
padding: 16px 16px;
}
`, `,
]; ];

View File

@ -21,6 +21,8 @@ export class CloudlySectionheading extends DeesElement {
h1 { h1 {
font-family: 'Cal Sans'; font-family: 'Cal Sans';
letter-spacing: 0.025em; letter-spacing: 0.025em;
margin: 0px;
margin-bottom: 16px;
} }
`, `,
] ]

View File

@ -0,0 +1,10 @@
import { css } from '@design.estate/dees-element';
export const viewHostCss = css`
:host {
display: block;
margin: auto;
max-width: 1280px;
padding: 16px 16px;
}
`;

View File

@ -1 +1,2 @@
export * from './cloudly-sectionheading.js'; export * from './cloudly-sectionheading.js';
export * from './css.js';