boto3 - base64 encoded lifecycle configuration produces instance failure - windows

I am trying to set up lifecycle configurations for Sagemaker notebooks over the aws api via boto3. From the docs it reads that a base64 encoded string of the configuration has to be provided.
I am using the following code:
with open(lifecycleconfig.sh, 'rb') as fp:
file_content = fp.read()
config_string = base64.b64encode(file_content).decode('utf-8')
boto3.client('sagemaker').create_notebook_instance_lifecycle_config(
NotebookInstanceLifecycleConfigName='mylifecycleconfig1',
OnCreate=[
{
'Content': config_string
},
],
)
With some lifecycleconfig.sh:
#!/bin/bash
set -e
This creates a lifecycle configuration which shows up in the web interface and whose content is seemingly identical to creating a config by hand:
image.
However Notebooks using the lifecycle config created via boto3 will not start and the log file will show error:
/home/ec2-user/SageMaker/create_script.sh: line 2: $'\r': command not found
/home/ec2-user/SageMaker/create_script.sh: line 3: set: -
set: usage: set [-abefhkmnptuvxBCHP] [-o option-name] [--] [arg ...]
Moreover, if I copy paste the content of the corrupted config and create a new config by hand, the new one will now also not start.
How do I have to encode a bash script for a working aws lifecycle configuration?

Found out that it is actually a Windows specific problem concerning the difference between open(..., 'rb').read() and open(..., 'r').read().encode('utf-8').
On my linux machine these two give the same result. On Windows however open(..., 'rb') gives stuff like \r\n for new lines, which appearantly can be comprehended by Amazon's web interface, but not the linux machine where the script gets deployed.
This is a os independent solution:
with open(lifecycleconfig.sh, 'r') as fp:
file_content = fp.read()
config_string = base64.b64encode(file_content.encode('utf-8')).decode('utf-8')

Related

PKCS12_parse: unsupported - when running a ruby application as a service on the server

In my ruby application, I am creating Apple wallet passes.
The application actually works well, but when I try to start it as a service (/etc/systemd/system), it is failing.
I can see that almost everything is working, but it fails when I want to parse the p12 certificate.
My function to sign the manifest file
def sign_manifest(serial_number)
temporary_path = "./passes/#{CUSTOMER}_#{serial_number}"
certificate_path = "./certs/Zertifikate.p12"
wwdr_path = "./certs/WWDR.pem"
manifest_path = "./passes/#{CUSTOMER}_#{serial_number}/manifest.json"
puts "Signing the manifest"
# Import the certificates
p12_certificate = OpenSSL::PKCS12::new(File.read(certificate_path), "")
wwdr_certificate = OpenSSL::X509::Certificate.new(File.read(wwdr_path))
# Sign the data
flag = OpenSSL::PKCS7::BINARY|OpenSSL::PKCS7::DETACHED
signed = OpenSSL::PKCS7::sign(p12_certificate.certificate, p12_certificate.key, File.read(manifest_path), [wwdr_certificate], flag)
# Create an output path for the signed data
signature_url = temporary_path + "/signature"
# Write out the data
File.open(signature_url, "w") do |f|
f.syswrite signed.to_der
end
end
Manually start with the command line
When I start the application manually with the command
ruby passGenerator.rb -p 20001 -o 0.0.0.0
on my server, it is working well, no issues.
Start as a service
The service itself looks like:
# wallet.service
[Unit]
Description = Apple Wallet Pass Generator
After = network.target
[Service]
WorkingDirectory = /var/www/html/passGenerator
ExecStart = ruby /var/www/html/passGenerator/passGenerator.rb -p 20001 -o 0.0.0.0
[Install]
WantedBy = multi-user.target
and start it with:
systemctl start wallet
I can start the service, and the server is running, but as soon as I want to create a new pass and come to this function, it crashes with the error:
PKCS12_parse: unsupported in the line of
p12_certificate = OpenSSL::PKCS12::new(File.read(certificate_path), "“)
(In the code snippet line 9)
I first thought about the relative paths, but everything else works with the relative paths.
Can anybody explain why that is happening?

Transferring google bucket file to end user without saving file locally

Right now when client download file from my site, I'm:
Downloading file from google cloud bucket to server (GCP download file, GCP streaming download)
Saving downloaded file to a Ruby Tempfile
sending Tempfile to enduser using Rails 5 send_file
I would like to skip step 2, to somehow transfer/stream file from google cloud to enduser without the file being saved at my server- is that possible?
Note the google bucket is private.
Code I'm currently using:
# 1 getting file from gcp:
storage = Google::Cloud::Storage.new
bucket = storage.bucket bucket_name, skip_lookup: true
gcp_file = bucket.file file_name
# 2a creates tempfile
temp_file = Tempfile.new('name')
temp_file_path = temp_file.path
# 2b populate tempfile with gcp file content:
gcp_file.download temp_file_path
# 3 sending tempfile to user
send_file(temp_file, type: file_mime_type, filename: 'filename.png')
What I would like:
# 1 getting file from gcp:
storage = Google::Cloud::Storage.new
bucket = storage.bucket bucket_name, skip_lookup: true
gcp_file = bucket.file file_name
# 3 sending/streaming file from google cloud to client:
send_file(gcp_file.download, type: file_mime_type, filename: 'filename.png')
Since making your objects or your bucket publicly readable or accessible is not an option for your project, the best option that I could suggest is using signed URLs so that you can still have control over your objects or bucket and also giving users sufficient permission to perform specific actions like download objects in your GCS bucket.

How to see print() results in Tarantool Docker container

I am using tarantool/tarantool:2.6.0 Docker image (the latest at the moment) and writing lua scripts for the project. I try to find out how to see the results of callin' print() function. It's quite difficult to debug my code without print() working.
In tarantool console print() have no effect also.
Using simple print()
Docs says that print() works to stdout, but I don't see any results when I watch container's logs by docker logs -f <CONTAINER_NAME>
I also tried to set container's logs driver to local. Than I get one time print to container's logs, but only once...
The container's /var/log directory is always empty.
Using box.session.push()
Using box.session.push() works fine in console, but when I use it in lua script:
-- app.lua
function log(s)
box.session.push(s)
end
-- No effect
log('hello')
function say_something(s)
log(s)
end
box.schema.func.create('say_something')
box.schema.user.grant('guest', 'execute', 'function', 'say_something')
And then call say_something() from nodeJs connector like this:
const TarantoolConnection = require('tarantool-driver');
const conn = new TarantoolConnection(connectionData);
const res = await conn.call('update_links', 'hello');
I get error:
Any suggestions?
Thanx!
I suppose you've missed io.flush() after print command.
After I added io.flush() after each print call my messages start to write to logs (docker logs -f <CONTAINER_NAME>).
Also I'd recommend to use log module for such purpose. It writes to stderr without buffering.
Regarding the error in the connector, I think nodejs connector simply doesn't support pushes.

How to properly make an api request in python3 with base64 encoding

I'm trying to run a bash script at server creation on vultr.com through the API using Python3
I'm not sure what I'm doing wrong. The server starts up but the script never runs.
The documentation states it has to be a base64 encoded string. I'm thinking I'm doing something wrong with the encoding.
Have any ideas?
import base64
import requests
key = 'redacted'
squid = '''#!/bin/bash
touch test'''
squid_encoded = base64.b64encode(squid.encode())
payload = {'DCID': 1, 'VPSPLANID': 29, 'OSID': 215, 'userdata': squid_encoded}
headers = {'API-Key': key}
def vult_api_call():
p = requests.post('https://api.vultr.com/v1/server/create', data=payload, headers=headers)
print(p.status_code)
print(p.text)
vult_api_call()
The cloud-init userdata scripts can be tricky to troubleshoot. Looking at your squid script, it is missing the #cloud-config header. Vultr has a similar example in their docs:
Run a script after the system boots.
#cloud-config
bootcmd:
- "/bin/echo sample > /root/my_file.txt"
</pre>
Support for this can vary by operating system though. I would recommend using Vultr's startup script feature instead of cloud-init, as it works for every operating system. These are referenced in the Vultr API spec with SCRIPTID.
Also note that "bootcmd:" will run every time the instance boots. There is also "runcmd:", but I have seen compatibility issues with it on some Ubuntu distros on Vultr.

How can multiple files be specified with "-files" in the CLI of Amazon for EMR?

I am trying to start an amazon cluster via the amazon CLI, but I am a little bit confused how I should specify multiple files. My current call is as follows:
aws emr create-cluster --steps Type=STREAMING,Name='Intra country development',ActionOnFailure=CONTINUE,Args=[-files,s3://betaestimationtest/mapper.py,-
files,s3://betaestimationtest/reducer.py,-mapper,mapper.py,-reducer,reducer.py,-
input,s3://betaestimationtest/output_0_inter,-output,s3://betaestimationtest/output_1_intra]
--ami-version 3.1.0
--instance-groupsInstanceGroupType=MASTER,InstanceCount=1,InstanceType=m3.xlarge
InstanceGroupType=CORE,InstanceCount=2,InstanceType=m3.xlarge --auto-terminate
--log-uri s3://betaestimationtest/logs
However, Hadoop now complains that it cannot find the reducer file:
Caused by: java.io.IOException: Cannot run program "reducer.py": error=2, No such file or directory
What am I doing wrong? The file does exist in the folder I specify
For passing multiple files in a streaming step, you need to use file:// to pass the steps as a json file.
AWS CLI shorthand syntax uses comma as delimeter to separate a list of args. So when we try to pass in parameters like: "-files","s3://betaestimationtest/mapper.py,s3://betaestimationtest/reducer.py", then the shorthand syntax parser will treat mapper.py and reducer.py files as two parameters.
The workaround is to use the json format. Please see the examples below.
aws emr create-cluster --steps file://./mysteps.json --ami-version 3.1.0 --instance-groups InstanceGroupType=MASTER,InstanceCount=1,InstanceType=m3.xlarge InstanceGroupType=CORE,InstanceCount=2,InstanceType=m3.xlarge --auto-terminate --log-uri s3://betaestimationtest/logs
mysteps.json looks like:
[
{
"Name": "Intra country development",
"Type": "STREAMING",
"ActionOnFailure": "CONTINUE",
"Args": [
"-files",
"s3://betaestimationtest/mapper.py,s3://betaestimationtest/reducer.py",
"-mapper",
"mapper.py",
"-reducer",
"reducer.py",
"-input",
" s3://betaestimationtest/output_0_inte",
"-output",
" s3://betaestimationtest/output_1_intra"
]}
]
You can also find examples here: https://github.com/aws/aws-cli/blob/develop/awscli/examples/emr/create-cluster-examples.rst. See example 13.
Hope it helps!
You are specifying -files twice, you only need to specify once. I forget if the CLI needs the separator to be a space or a comma for multiple values, but you can try that out.
You should replace:
Args=[-files,s3://betaestimationtest/mapper.py,-files,s3://betaestimationtest/reducer.py,-mapper,mapper.py,-reducer,reducer.py,-input,s3://betaestimationtest/output_0_inter,-output,s3://betaestimationtest/output_1_intra]
with:
Args=[-files,s3://betaestimationtest/mapper.py s3://betaestimationtest/reducer.py,-mapper,mapper.py,-reducer,reducer.py,-input,s3://betaestimationtest/output_0_inter,-output,s3://betaestimationtest/output_1_intra]
or if that fails, with:
Args=[-files,s3://betaestimationtest/mapper.py,s3://betaestimationtest/reducer.py,-mapper,mapper.py,-reducer,reducer.py,-input,s3://betaestimationtest/output_0_inter,-output,s3://betaestimationtest/output_1_intra]
Add an escape for comma separating files:
Args=[-files,s3://betaestimationtest/mapper.py\\,s3://betaestimationtest/reducer.py,-mapper,mapper.py,-reducer,reducer.py,-input,s3://betaestimationtest/output_0_inter,-output,s3://betaestimationtest/output_1_intra]

Resources