Browse Source

storage-cli: update to work with any storage provider

Mokhtar Naamani 4 years ago
parent
commit
746c6f0a63

+ 2 - 2
.github/workflows/run-network-tests.yml

@@ -221,10 +221,10 @@ jobs:
         run: |
         run: |
           sleep 6
           sleep 6
           export DEBUG=joystream:*
           export DEBUG=joystream:*
-          yarn storage-cli upload ./pioneer/packages/apps/public/images/default-thumbnail.png 1 0
+          yarn storage-cli upload ./tests/network-tests/assets/joystream.MOV 1 0
           # Wait for storage-node to set status Accepted on uploaded content
           # Wait for storage-node to set status Accepted on uploaded content
           sleep 6
           sleep 6
           cd utils/api-scripts/
           cd utils/api-scripts/
           # Assume only one accepted data object was created
           # Assume only one accepted data object was created
           CONTENT_ID=`yarn --silent script get-first-content-id | tail -n2 | head -n1`
           CONTENT_ID=`yarn --silent script get-first-content-id | tail -n2 | head -n1`
-          yarn storage-cli download http://localhost:3001 ${CONTENT_ID} ./download.png
+          yarn storage-cli download ${CONTENT_ID} ./joystream.mov

+ 3 - 7
storage-node/packages/cli/src/cli.ts

@@ -41,8 +41,7 @@ const usage = `
     upload            Upload a file to the Joystream Network. Requires a
     upload            Upload a file to the Joystream Network. Requires a
                       source file path to upload, data object ID, member ID and account key file with
                       source file path to upload, data object ID, member ID and account key file with
                       pass phrase to unlock it.
                       pass phrase to unlock it.
-    download          Retrieve a file. Requires a storage node URL and a content
-                      ID, as well as an output filename.
+    download          Retrieve a file. Requires a content and an output filename.
     head              Send a HEAD request for a file, and print headers.
     head              Send a HEAD request for a file, and print headers.
                       Requires a storage node URL and a content ID.
                       Requires a storage node URL and a content ID.
 
 
@@ -88,11 +87,8 @@ const commands = {
 
 
     await uploadCmd.run()
     await uploadCmd.run()
   },
   },
-  // needs to be updated to take a content id and resolve it a potential set
-  // of providers that has it, and select one (possibly try more than one provider)
-  // to fetch it from the get api url of a provider..
-  download: async (api: any, url: string, contentId: string, filePath: string) => {
-    const downloadCmd = new DownloadCommand(api, url, contentId, filePath)
+  download: async (api: any, contentId: string, filePath: string) => {
+    const downloadCmd = new DownloadCommand(api, contentId, filePath)
 
 
     await downloadCmd.run()
     await downloadCmd.run()
   },
   },

+ 40 - 0
storage-node/packages/cli/src/commands/base.ts

@@ -1,9 +1,17 @@
 import chalk from 'chalk'
 import chalk from 'chalk'
 import removeEndingForwardSlash from '@joystream/storage-utils/stripEndingSlash'
 import removeEndingForwardSlash from '@joystream/storage-utils/stripEndingSlash'
 import { ContentId } from '@joystream/types/storage'
 import { ContentId } from '@joystream/types/storage'
+import Debug from 'debug'
+const debug = Debug('joystream:storage-cli:base')
 
 
 // Commands base abstract class. Contains reusable methods.
 // Commands base abstract class. Contains reusable methods.
 export abstract class BaseCommand {
 export abstract class BaseCommand {
+  protected readonly api: any
+
+  constructor(api: any) {
+    this.api = api
+  }
+
   // Creates the Colossus asset URL and logs it.
   // Creates the Colossus asset URL and logs it.
   protected createAndLogAssetUrl(url: string, contentId: string | ContentId): string {
   protected createAndLogAssetUrl(url: string, contentId: string | ContentId): string {
     let normalizedContentId: string
     let normalizedContentId: string
@@ -50,4 +58,36 @@ export abstract class BaseCommand {
     // Maximum content length for the assets (files)
     // Maximum content length for the assets (files)
     return 2000 * 1024 * 1024
     return 2000 * 1024 * 1024
   }
   }
+
+  // Requests the runtime and obtains the storage node endpoint URL.
+  protected async getStorageProviderEndpoint(storageProviderId: string): Promise<string> {
+    try {
+      const endpoint = await this.api.workers.getWorkerStorageValue(storageProviderId)
+
+      debug(`Resolved endpoint: ${endpoint}`)
+
+      return endpoint
+    } catch (err) {
+      this.fail(`Could not get provider endpoint: ${err}`)
+    }
+  }
+
+  protected async getAnyProviderEndpoint(): Promise<string> {
+    try {
+      const providers = await this.api.workers.getAllProviders()
+
+      debug(`Available Providers: ${providers}`)
+      // select first provider
+      do {
+        const id = providers.ids.pop()
+        const endpoint = await this.getStorageProviderEndpoint(id)
+        if (endpoint) {
+          return endpoint
+        }
+      } while (providers.ids.length)
+      throw new Error('No Providers registered endpoint')
+    } catch (err) {
+      this.fail(`Could not get provider endpoint: ${err}`)
+    }
+  }
 }
 }

+ 8 - 17
storage-node/packages/cli/src/commands/download.ts

@@ -5,38 +5,27 @@ import { BaseCommand } from './base'
 
 
 // Download command class. Validates input parameters and execute the logic for asset downloading.
 // Download command class. Validates input parameters and execute the logic for asset downloading.
 export class DownloadCommand extends BaseCommand {
 export class DownloadCommand extends BaseCommand {
-  private readonly api: any
-  private readonly storageNodeUrl: string
   private readonly contentId: string
   private readonly contentId: string
   private readonly outputFilePath: string
   private readonly outputFilePath: string
 
 
-  constructor(api: any, storageNodeUrl: string, contentId: string, outputFilePath: string) {
-    super()
+  constructor(api: any, contentId: string, outputFilePath: string) {
+    super(api)
 
 
-    this.api = api
-    this.storageNodeUrl = storageNodeUrl
     this.contentId = contentId
     this.contentId = contentId
     this.outputFilePath = outputFilePath
     this.outputFilePath = outputFilePath
   }
   }
 
 
   // Provides parameter validation. Overrides the abstract method from the base class.
   // Provides parameter validation. Overrides the abstract method from the base class.
   protected validateParameters(): boolean {
   protected validateParameters(): boolean {
-    return (
-      this.storageNodeUrl &&
-      this.storageNodeUrl !== '' &&
-      this.contentId &&
-      this.contentId !== '' &&
-      this.outputFilePath &&
-      this.outputFilePath !== ''
-    )
+    return this.contentId && this.contentId !== '' && this.outputFilePath && this.outputFilePath !== ''
   }
   }
 
 
   // Shows command usage. Overrides the abstract method from the base class.
   // Shows command usage. Overrides the abstract method from the base class.
   protected showUsage() {
   protected showUsage() {
     console.log(
     console.log(
       chalk.yellow(`
       chalk.yellow(`
-        Usage:   storage-cli download colossusURL contentID filePath
-        Example: storage-cli download http://localhost:3001 0x7a6ba7e9157e5fba190dc146fe1baa8180e29728a5c76779ed99655500cff795 ./movie.mp4
+        Usage:   storage-cli download contentID filePath
+        Example: storage-cli download 0x7a6ba7e9157e5fba190dc146fe1baa8180e29728a5c76779ed99655500cff795 ./movie.mp4
       `)
       `)
     )
     )
   }
   }
@@ -46,7 +35,9 @@ export class DownloadCommand extends BaseCommand {
     // Checks for input parameters, shows usage if they are invalid.
     // Checks for input parameters, shows usage if they are invalid.
     if (!this.assertParameters()) return
     if (!this.assertParameters()) return
 
 
-    const assetUrl = this.createAndLogAssetUrl(this.storageNodeUrl, this.contentId)
+    const storageNodeUrl = await this.getAnyProviderEndpoint()
+
+    const assetUrl = this.createAndLogAssetUrl(storageNodeUrl, this.contentId)
     console.log(chalk.yellow('File path:', this.outputFilePath))
     console.log(chalk.yellow('File path:', this.outputFilePath))
 
 
     // Create file write stream and set error handler.
     // Create file write stream and set error handler.

+ 1 - 3
storage-node/packages/cli/src/commands/head.ts

@@ -4,14 +4,12 @@ import { BaseCommand } from './base'
 
 
 // Head command class. Validates input parameters and obtains the asset headers.
 // Head command class. Validates input parameters and obtains the asset headers.
 export class HeadCommand extends BaseCommand {
 export class HeadCommand extends BaseCommand {
-  private readonly api: any
   private readonly storageNodeUrl: string
   private readonly storageNodeUrl: string
   private readonly contentId: string
   private readonly contentId: string
 
 
   constructor(api: any, storageNodeUrl: string, contentId: string) {
   constructor(api: any, storageNodeUrl: string, contentId: string) {
-    super()
+    super(api)
 
 
-    this.api = api
     this.storageNodeUrl = storageNodeUrl
     this.storageNodeUrl = storageNodeUrl
     this.contentId = contentId
     this.contentId = contentId
   }
   }

+ 2 - 17
storage-node/packages/cli/src/commands/upload.ts

@@ -21,7 +21,6 @@ interface AddContentParams {
 
 
 // Upload command class. Validates input parameters and uploads the asset to the storage node and runtime.
 // Upload command class. Validates input parameters and uploads the asset to the storage node and runtime.
 export class UploadCommand extends BaseCommand {
 export class UploadCommand extends BaseCommand {
-  private readonly api: any
   private readonly mediaSourceFilePath: string
   private readonly mediaSourceFilePath: string
   private readonly dataObjectTypeId: string
   private readonly dataObjectTypeId: string
   private readonly keyFile: string
   private readonly keyFile: string
@@ -36,9 +35,8 @@ export class UploadCommand extends BaseCommand {
     keyFile: string,
     keyFile: string,
     passPhrase: string
     passPhrase: string
   ) {
   ) {
-    super()
+    super(api)
 
 
-    this.api = api
     this.mediaSourceFilePath = mediaSourceFilePath
     this.mediaSourceFilePath = mediaSourceFilePath
     this.dataObjectTypeId = dataObjectTypeId
     this.dataObjectTypeId = dataObjectTypeId
     this.memberId = memberId
     this.memberId = memberId
@@ -153,19 +151,6 @@ export class UploadCommand extends BaseCommand {
     }
     }
   }
   }
 
 
-  // Requests the runtime and obtains the storage node endpoint URL.
-  private async discoverStorageProviderEndpoint(storageProviderId: string): Promise<string> {
-    try {
-      const endpoint = await this.api.workers.getWorkerStorageValue(storageProviderId)
-
-      debug(`Resolved endpoint: ${endpoint}`)
-
-      return endpoint
-    } catch (err) {
-      this.fail(`Could not get provider endpoint: ${err}`)
-    }
-  }
-
   // Loads and unlocks the runtime identity using the key file and pass phrase.
   // Loads and unlocks the runtime identity using the key file and pass phrase.
   private async loadIdentity(): Promise<any> {
   private async loadIdentity(): Promise<any> {
     const noKeyFileProvided = !this.keyFile || this.keyFile === ''
     const noKeyFileProvided = !this.keyFile || this.keyFile === ''
@@ -208,7 +193,7 @@ export class UploadCommand extends BaseCommand {
     const dataObject = await this.createContent(addContentParams)
     const dataObject = await this.createContent(addContentParams)
     debug(`Received data object: ${dataObject.toString()}`)
     debug(`Received data object: ${dataObject.toString()}`)
 
 
-    const colossusEndpoint = await this.discoverStorageProviderEndpoint(dataObject.liaison.toString())
+    const colossusEndpoint = await this.getAnyProviderEndpoint()
     debug(`Discovered storage node endpoint: ${colossusEndpoint}`)
     debug(`Discovered storage node endpoint: ${colossusEndpoint}`)
 
 
     const assetUrl = this.createAndLogAssetUrl(colossusEndpoint, addContentParams.contentId)
     const assetUrl = this.createAndLogAssetUrl(colossusEndpoint, addContentParams.contentId)