Get StdOut from RunCommand using Azure VM Javascript SDK - javascript

I'm using the Azure VM Javascript SDK in my Node.js webApp. I'm trying to use the RunCommand Function to run a custom script on my Azure virtual machines.
The problem I'm having is with obtaining the response from running the command which should contain the StdOut and StdErr strings.
If I run the command from the Azure CLI, Like the following:
az vm run-command invoke -g 'myResource' -n 'myVm' --command-id RunPowerShellScript --scripts 'Get-ChildItem -Name'
Then I am able to receive the response, e.g. (notice in the 'message' section there is a listing of the files returned from 'Get-ChildItem')
{
"value": [
{
"code": "ComponentStatus/StdOut/succeeded",
"displayStatus": "Provisioning succeeded",
"level": "Info",
"message": "myTxtFile.txt\nscript12.ps1\nscript13.ps1\nscript14.ps1\nscript15.ps1\nscript16.ps1\nscript17.ps1\nscript18.ps1\nscript19.ps1\nscript20.ps1\nscript21.ps1\nscript22.ps1\nscript23.ps1\nscript24.ps1\nscript25.ps1\nscript26.ps1",
"time": null
},
{
"code": "ComponentStatus/StdErr/succeeded",
"displayStatus": "Provisioning succeeded",
"level": "Info",
"message": "",
"time": null
}
]
}
However, when I run this code from the javascript SDK I don't get any thing returned. Here is the code:
let usr = ...;
let pas = ...;
let subscriptionId = ...;
let client = null;
msRestAzure.loginWithUsernamePassword(usr, pas, function(err, credentials) {
if (err) return console.log(err);
client = new azureArmCompute.ComputeManagementClient(credentials, subscriptionId);
let param = {
commandId: 'RunPowerShellScript',
script: [
'echo $null >> myTxtFile.txt\n' +
'Get-ChildItem -Name\n'
]
};
client.virtualMachines.runCommand('myResource', 'myVm', param, function (err, result) {
console.log(err);
console.log(result);
})
});
and here is what is printed to the console:
null
{}
I know that the script is actually running because I tried, and was successfully able, to create a text file (myTxtFile.txt).
Anyone have any clues as to why I'm not getting anything in the result object?
Edit 1 (in response to #Itay):
Looking at the source, the callback is supposed to be a "ServiceCallback" of type "RunCommandResult". Here are the three function declarations for RunCommand.
Here's the declaration for the ServiceCallback interface.
So in my callback I was expecting there to be four returns "err" and "result" and "request" and "response" (I'm obviously leaving off the latter two). In my example I'm printing the error and result objects to the console, but the result object doesn't have anything in it...

I think the signature for the callback you defined is not as described by the documentation.
Looking at runCommand(string, string, RunCommandInput, ServiceCallback< RunCommandResult >), it seems that the callback should be accepting a RunCommandResult interface, which in turn, contains a property called Value which is an array of InstanceViewStatus interface instances that might hold the information you are looking for.
Hope it helps!

Related

Cannot map variable from data stream to users identified response while developing voice app

I am currently developing a voice app with Google Actions where users are able to ask for information about items in a list that is provided through a file stream with Axios as shown in the following LINK. The data looks like this:
[
{
"Name": "Beam",
"Level": "2",
"Zone": "A",
"Location": "Beam is located on Level 2 in zone A",
"Responsible": "Contractor"
},
{
"Name": "Column",
"Level": "3",
"Zone": "A",
"Location": "Column is located on Level 3 in zone A",
"Responsible": "Kiewit"
},
{
"Name": "Window",
"Level": "2",
"Zone": "B",
"Location": "Window is located on Level 2 in zone B",
"Responsible": "Tech"
}
]
Here, it shows three items being a BEam, a Column, and a Window so the objective is that users ask about one of the items and the voice app will provide the other information such as Level, ZOne, Location, or Responsible to the user.
To complete this, I am using the web interface of Google Actions and using inline cloud functions as webhooks in Google Actions that looks like this:
const { conversation } = require('#assistant/conversation');
const functions = require('firebase-functions');
require('firebase-functions/lib/logger/compat'); // console.log compact
const axios = require('axios');
const app = conversation({debug: true});
app.handle('getItem', async conv => {
const data = await getItem();
const itemParam = conv.intent.params.Item.resolved;
// console.log(itemParam);
// conv.add(`This test to see if we are accessing the webhook for ${itemParam}`);
data.map(item => {
if (item.Name === itemParam);
conv.add(`These are the datails for ${itemParam}. It is located in zone ${item.Zone}, at level ${item.Level}`);
// conv.add(`This test to see if we are accessing the webhook for ${item.Name}`);
});
});
async function getItem() {
const res = await axios.get('https://sheetdb.io/api/v1/n3ol4hwmfsmqd');
console.log(res.data);
return res.data; // To use in your Action's response
}
exports.ActionsOnGoogleFulfillment = functions.https.onRequest(app);
When I check the console logs, I can see that I am retrieving the data in a single event as provided in the LINK. Also, the recognition of the item name is working in the app by defining a type within the app to be recognized based on type categories. And this information is being stored in ItemParam.
However, the main issue I have right now is to link both things together. I was trying to use a map function to match the itemParam and the Item.Name from the data stream. However, this is not working at al. The function I was trying to do is:
data.map(item => {
if (item.Name === itemParam);
conv.add(`These are the datails for ${itemParam}. It is located in zone ${item.Zone}, at level ${item.Level}`);
What I am trying to do here is when the function detects that the user ItemParam is matched to the Item in the stream, use the information from that stream item and add a phrase to the conversation that includes the ItemParam, and the other information about that same item.
Besides, the way this function is right now, also shoots this error:
cf-GPfYHj4HKDWGvHKWArq34w-name
Error: Error adding simple response: **Two simple responses already defined**
at addSimple (/workspace/node_modules/#assistant/conversation/dist/conversation/prompt/prompt.js:34:15)
at Prompt.add (/workspace/node_modules/#assistant/conversation/dist/conversation/prompt/prompt.js:108:17)
at ConversationV3.add (/workspace/node_modules/#assistant/conversation/dist/conversation/conv.js:102:21)
at data.map.item (/workspace/index.js:16:13)
at Array.map (<anonymous>) at app.handle (/workspace/index.js:14:8) at process._tickCallback (internal/process/next_tick.js:68:7)
I am honestly not that familiar with Javascript and I might be doing silly mistakes but I really cannot figure out this.
Any help will be much appreciated. Thank you
The error you are seeing is:
Error: Error adding simple response: Two simple responses already defined
Your action's response can only include two simple responses. Each response is rendered as a separate text bubble on a phone, for instance.
So it seems like the item.Name === itemParam is true multiple times and you end up creating too many responses.
Why does this happen? It comes from how your conditional is written:
data.map(item => {
if (item.Name === itemParam);
conv.add(`These are the datails for ${itemParam}. It is located in zone ${item.Zone}, at level ${item.Level}`);
});
You have correctly identified that the ; semicolon character denotes the end of a statement. However, this does not apply to if statements. Because the way it's written, you have this conditional and then conclude it before you actually run conv.add. This means that conv.add escapes your check and runs for every item. If you were to log the conv response, you'd see a bunch of text.
To fix it, keep in mind that a conditional needs to wrap the surrounding code. This is done with curly braces { & }.
data.map(item => {
if (item.Name === itemParam) {
conv.add(`These are the datails for ${itemParam}. It is located in zone ${item.Zone}, at level ${item.Level}`);
}
});
You can even see this in the map method, where the mapping logic surrounds your if-statement with curly braces. This shows that one is contained entirely within the other.
Thanks, Nick, I fixed my function based on your feedback and now I understand a little better about the map function. Another issue that I figured out along the way was that upper and lower case does matter to match the map function so I also had to modify the type to lowercase and add .toLowerCase() methods to variables.
Now my code is working with two variables Item and Item_ID so if the user asks about a generic item, it can get detailed by adding the ID of the item to the query question.
Now my code looks like this:
// From here, there are all the required libraries to be loaded
const { conversation } = require('#assistant/conversation'); // This the app coversation
const functions = require('firebase-functions'); //These are the firebase functions
require('firebase-functions/lib/logger/compat'); // console.log compact
const axios = require('axios'); // This is axios to retrieve the data stream
// To here, there all the required libraries to be loaded
const app = conversation({debug: true}); // This instantiate the conversation
/* This function retrieve the data from the file stream */
async function getItem() {
const res = await axios.get('https://sheetdb.io/api/v1/n3ol4hwmfsmqd');
return res.data; // To use in your Action's response
}
/* This is the fuction to match user's responses and data stream*/
app.handle('getItem', async conv => { //getItem is the weekhook name used in Google Actions, conv is the conversation
const data = await getItem(); // Here the data stream is retrieved and send to the data variable
// console.log(data);
const itemParam = conv.intent.params.Item.resolved; // This is the user's response, in other words, what item the user's want to know from the data.
const itemIDParam = conv.intent.params.Item_ID.resolved.replace(/\s/g, ''); //This is the user's response for item ID
const itemFromUser = itemParam + " " + itemIDParam;
console.log(itemParam);
console.log(itemIDParam);
console.log(itemFromUser);
// conv.add(`This test to see if we are accessing the webhook for ${itemParam}`); // This is to know if I was getting the correct item from the user. Currently this is working
// console.log(data);
data.map(item => { //Then, I am trying to map the data stream to recognize the data headers and identify items
// console.log(data);
// console.log(item);
if (item.Name.toLowerCase() === itemFromUser.toLowerCase()){
console.log(item);
conv.add(`These are the details for ${itemFromUser}. It is located in zone ${item.Zone}, at level ${item.Level}.`);
// console.log(conv);
// console.log(data);
}
else {
conv.add(`I am sorry. I could not find any information about that object. Please try with another construction object.`);
}
});
});
exports.ActionsOnGoogleFulfillment = functions.https.onRequest(app);
Now I can handle most of the questions except when something is not in the data stream which makes the app to show me this error:
"error": "Error adding simple response: Two simple responses already defined"
This is the same error as I was getting before and I am not sure how to fix it yet. I tried to implement an else statement for that condition as follows:
else {
conv.add(`I am sorry. I could not find any information about that object. Please try with another construction object.`);
}
But I am still getting same error.
I am still working on this.

Js-IPFS Error: cid.toBaseEncodedString() is not a function

I'm currently working on the backend of a website that would work similar to YouTube but only use IPFS for storage, meaning if you want to "upload" videos to the site it would already have to be on the IPFS network. The actual function is more of an Index than anything else but it was something that I wanted to tackle.
The section I'm working on is intended to verify the integrity of the CID hashes by making sure that there are still providers on the network for that specific content. If there aren't any then the CID and any information associated will get removed from my database but I'm currently getting an issue when trying using the ipfs.dhs.findProvs function.
Here is part of my code:
const ipfs = await IPFS.create({
libp2p: { config: { dht: { enabled: true } } },
});
for (var i of integrityData) {
let cid = new CID(i.CID);
console.log(cid);
let providers = ipfs.dht.findProvs(cid, { numProviders: 2 });
for await (const provider of providers) {
console.log(provider);
}
}
Error Log:
C:\Users\...\node_modules\libp2p-kad-dht\src\providers.js:202
this._log('getProviders %s', cid.toBaseEncodedString())
^
TypeError: cid.toBaseEncodedString is not a function
To further explain my code, the for loops is iterating the JSON content received the Database after querying for all the CIDs in it. i.CID does return the correct CID as a string which I then create a CID object from and pass to the function here ipfs.dht.findProvs(cid, { numProviders: 2 });. The nested for loop is there to iterate through the object that is received but I haven't made it to that stage as I keep getting the same error.

Azure Functions - Value cannot be null. (Parameter 'connectionString')

I was trying to setup simple Azure Function to read a XML stream and sync it back to the database. My plan was to use a Time Trigger to execute the function once per day.
Howerver, things are not looking great. I'm getting the following error, even if I don't use a database:
[Error] Executed 'Functions.<func-name>' (Failed, Id=<func-id>, Duration=1ms)Value cannot be null. (Parameter 'connectionString')
I'm currently trying to execute the following function:
module.exports = async function(context, req) {
context.res = {
body: "Success!"
};
};
Same result. I can't run it.
I've added a Connection String to the Configuration -> Connection Strings (I thought that I've missed that, based on the message).
My functions.json file looks like:
{
"bindings": [
{
"name": "myTimer",
"type": "timerTrigger",
"direction": "in",
"schedule": "0 0 * * * *"
},
{
"type": "http",
"direction": "out",
"name": "res"
}
]
}
I've also tried running a C# function - same result.
So, what have I missed?
Logging from Microsoft to it's finest.
AzureWebJobsStorage App Setting was missing.
Solution:
Create Storage account (or use existing one)
Go to your Function App's Configuration
Add AzureWebJobsStorage with a connection string to your Storage account (can be found at Storage Account Overview -> Access Keys)
In my case the error was Microsoft.Extensions.Configuration.AzureAppConfiguration value cannot be null: parameter (connectionString)
This happened because I has installed Microsoft.Extensions.Configuration.AzureAppConfiguration in my Function to DI the configuration into my main function. The Startup.cs line string cs = Environment.GetEnvironmentVariable("MyDifferentConnectionString"); was not able to find an environment variable for MyDifferentConnectionString, so this needed to be added to the Function config.
Go to App Configuration (or create one)
Access Keys (under Settings)
Copy Connection String
Go to your Function
Configuration (under Settings)
Add a new Application Settings with the name of your environment variable and paste the value
Save and restart your Function

How to run mongodb shell query from c# using mongodb driver

I want to run below mongo query from C#. Currently I am using mongodb C# driver 2.7
db.changelog.find({}).forEach(function(doc){
//make javascript object dynimacally
var newDoc = {
"key": "value",
"doc": doc
}
db.changelog_log.insertOne(newDoc); })
This is successfully run into mongo shell, now I want to execute that script from c# application with modify object value dynamically. I was hoping it would be able to parse those queries and pass them off to database.RunCommand but I have not been able to make any progress.
var bsonDoc = MongoDB.Bson.Serialization.BsonSerializer.Deserialize<BsonDocument>("query");
database.RunCommand(command);
That code fails on the call to Deserialize with this error message: [Additional information: JSON reader was expecting a value but found 'db'.] which makes perfect sense because the script is not valid JSON.
After mongo 3.0 eval function has been depreciated.
So I'm having trouble parsing the script into something that can be executed.
VehicleModel is a model which I am using for inserting and same for retrieving,
so it replaces with your model
var collection = database.GetCollection<VehicleModel>("VehicleModel");
var Data = await collection.Find(Builders<VehicleModel>.Filter.Empty).ToListAsync();
foreach(VehicleModel vm in Data)
{
var newDoc =
{
"key": "value",
"doc": vm
collection.InsertOneAsync(value);
}
}
Maybe you are looking for this

Using elasticsearch.js

I've run elasticsearch.bat and queried this in browser with localhost:9200. JSON object returned as follows, so all ok so far.
{
"status" : 200,
"name" : "hwils_01_dev",
"cluster_name" : "elasticsearch_hwils_dev",
"version" : {
"number" : "1.7.2",
"build_hash" : "e43676b1385b8125d647f593f7202acbd816e8ec",
"build_timestamp" : "2015-09-14T09:49:53Z",
"build_snapshot" : false,
"lucene_version" : "4.10.4"
},
"tagline" : "You Know, for Search"
}
I've downloaded and linked via tag to my index.html the elasticsearch.js
<script src="elasticsearch-js/elasticsearch.js"></script>
(incidentally there's more than 1 .js in the download - do I need to link them all?)
I've then run added to another tag the code
var client = new elasticsearch.Client({
host: 'localhost:9200',
log: 'trace'
});
and output this to console - JSON object returned so again, presumably all ok up to this point.
If I then run
client.ping({
requestTimeout: 30000,
// undocumented params are appended to the query string
hello: 'elasticsearch'
}, function (error) {
if (error) {
console.error('elasticsearch cluster is down!');
} else {
console.log('All is well');
}
});
I get the error message returned. And I don't know why.
Incidentally
var elasticsearch = require('elasticsearch');
returns "Uncaught reference error: require is not defined" which would suggest I'm missing at least one other .js file with that function?
Since you're in a browser, you need to use a browser build (at the moment Angular or jQuery are available).
So let's say you're going the jQuery way, your <script> should look like this:
<script src="elasticsearch-js/elasticsearch.jquery.min.js"></script>
And then you can create your client like this:
var client = new $.es.Client({
hosts: 'localhost:9200',
log: 'trace'
});
The require statement is a NodeJS only function. You need to download and reference the browser build of ES in your HTMl file as Val mentioned.

Categories