Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

deploy error: accepts at most 1 arg(s), received 3 - docker login ? #538

Open
hervejegou opened this issue Feb 5, 2021 · 5 comments
Open

Comments

@hervejegou
Copy link

Hi Jonathan:

When deploying with ssocketcluster deploy I get an error after image is built. it fails at 'docker login' apparently:

=> exporting to image                                                       0.0s 
=> => exporting layers                                                      0.0s 
=> => writing image sha256:145631044140023b405ff54e34796fb85a2a12f35ccad22  0.0s 
=> => naming to docker.io/ciliconherve/signal-skytalk:v1.0.0                0.0s
accepts at most 1 arg(s), received 3
[Error] Failed to deploy the 'signal-skytalk' app. Command failed: docker login -u "...myid" -p "...mypassword"; docker push 
ciliconherve/signal-skytalk:v1.0.0

If I run the docker login command by itself, it succeeds (with a warning:

docker login -u "...myid" -p "...mypassword"
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
Login Succeeded

PROBLEM:

I never had this issue before (we have used socketcluster for last one year - works great by the way !) and I searched online and do not seem to find where this come from.

Also I am able to run these 2 commands manually:

docker login -u "...myid" -p "...mypassword"
docker push ciliconherve/signal-skytalk:v1.0.0

ALTERNATIVE FIX:

Would you have the list of commands to perform 'socketcluster deploy' manually so that I can perform the initial deploy manually and hopefully the 'socketcluster update' will work ?

@jondubois
Copy link
Member

jondubois commented Feb 5, 2021

@hervejegou Strange. I have not made any changes to the deployment process in a long time. Maybe this issue is caused by an update to kubectl or docker.

If doing it manually, there are two ways to deploy:

1. Deploy the source code in an app-src-container, then let Kubernetes attach it to the scc-worker container

This is how the socketcluster deploy command works. It takes up the least amount of space and bandwidth since it only pushes the source code and its npm dependencies in its own container instead of the entire Node.js + SC environment to DockerHub. But it's trickier to perform since the container names have to match up so that Kubernetes can automatically attach the source code container to the scc-worker container (the source container is an init container).

You should read the code here:

socketcluster/bin/cli.js

Lines 496 to 752 in 7b1d97c

} else if (command === 'deploy' || command === 'deploy-update') {
let dockerImageName, dockerDefaultImageName;
let dockerDefaultImageVersionTag = 'v1.0.0';
let nextVersionTag;
let appPath = arg1 || '.';
let absoluteAppPath = path.resolve(appPath);
let pkg = parsePackageFile(appPath);
let appName = pkg.name;
let isUpdate = (command === 'deploy-update');
let targetCPUUtilization = 50;
let maxPodsPerService = 10;
let failedToDeploy = function (err) {
errorMessage(`Failed to deploy the '${appName}' app. ${err.message}`);
process.exit();
};
let socketClusterK8sConfigFilePath = appPath + '/socketcluster-k8s.json';
let socketClusterK8sConfig = parseJSONFile(socketClusterK8sConfigFilePath);
let addAuthDetailsToSocketClusterK8s = function (socketClusterK8sConfigJSON, username, password) {
if (!socketClusterK8sConfigJSON.docker) {
socketClusterK8sConfigJSON.docker = {};
}
socketClusterK8sConfigJSON.docker.auth = Buffer.from(`${username}:${password}`, 'utf8').toString('base64');
};
let saveSocketClusterK8sConfigFile = function (socketClusterK8sConfigJSON) {
fs.writeFileSync(socketClusterK8sConfigFilePath, JSON.stringify(socketClusterK8sConfigJSON, null, 2));
};
let parseVersionTag = function (fullImageName) {
let matches = fullImageName.match(/:[^:]*$/);
if (!matches) {
return '';
}
return matches[0] || '';
};
let setImageVersionTag = function (imageName, versionTag) {
if (versionTag.indexOf(':') !== 0) {
versionTag = ':' + versionTag;
}
return imageName.replace(/(\/[^\/:]*)(:[^:]*)?$/g, `$1${versionTag}`);
};
let promptDockerAuthDetails = function (callback) {
let handleSaveDockerAuthDetails = function (saveAuthDetails) {
saveDockerAuthDetails = saveAuthDetails;
callback(dockerUsername, dockerPassword, saveDockerAuthDetails);
};
let promptSaveAuthDetails = function () {
promptConfirm(`Would you like to save your Docker registry username and password as Base64 to ${socketClusterK8sConfigFilePath}?`, {default: true}, handleSaveDockerAuthDetails);
};
let handlePassword = function (password) {
dockerPassword = password;
if (saveDockerAuthDetails != null) {
handleSaveDockerAuthDetails(saveDockerAuthDetails);
return;
}
promptSaveAuthDetails();
};
let handleUsername = function (username) {
dockerUsername = username;
if (dockerPassword != null) {
handlePassword(dockerPassword);
return;
}
promptInput('Enter your Docker registry password:', handlePassword, true);
};
let promptUsername = function () {
if (dockerUsername != null) {
handleUsername(dockerUsername);
return;
}
promptInput('Enter your Docker registry username:', handleUsername);
};
promptUsername();
};
let performDeployment = function (dockerConfig, versionTag, username, password) {
let dockerLoginCommand = `docker login -u "${username}" -p "${password}"`;
let fullVersionTag;
if (versionTag) {
fullVersionTag = `:${versionTag}`;
} else {
fullVersionTag = parseVersionTag(dockerConfig.imageName);
}
dockerConfig.imageName = setImageVersionTag(dockerConfig.imageName, fullVersionTag);
if (saveDockerAuthDetails) {
addAuthDetailsToSocketClusterK8s(socketClusterK8sConfig, username, password);
}
try {
saveSocketClusterK8sConfigFile(socketClusterK8sConfig);
execSync(`docker build -t ${dockerConfig.imageName} .`, {stdio: 'inherit'});
execSync(`${dockerLoginCommand}; docker push ${dockerConfig.imageName}`, {stdio: 'inherit'});
if (tlsSecretName && tlsKeyPath && tlsCertPath) {
uploadTLSSecret(tlsSecretName, tlsKeyPath, tlsCertPath, warningMessage);
}
let kubernetesDirPath = appPath + '/kubernetes';
let kubeConfSCCWorker = getSCCWorkerDeploymentDefPath(kubernetesDirPath);
let kubeConfContentSCCWorker = fs.readFileSync(kubeConfSCCWorker, {encoding: 'utf8'});
let deploymentConfSCCWorker = YAML.parse(kubeConfContentSCCWorker);
let initContainersSCCWorker = deploymentConfSCCWorker.spec.template.spec.initContainers;
initContainersSCCWorker.forEach((value, index) => {
if (value) {
if (value.name === 'app-src-container') {
initContainersSCCWorker[index].image = dockerConfig.imageName;
}
}
});
let formattedYAMLStringSCCWorker = sanitizeYAML(YAML.stringify(deploymentConfSCCWorker, Infinity, 2));
fs.writeFileSync(kubeConfSCCWorker, formattedYAMLStringSCCWorker);
let kubeConfSCCBroker = getSCCBrokerDeploymentDefPath(kubernetesDirPath);
let kubeConfContentSCCBroker = fs.readFileSync(kubeConfSCCBroker, {encoding: 'utf8'});
let deploymentConfSCCBroker = YAML.parse(kubeConfContentSCCBroker);
let formattedYAMLStringSCCBroker = sanitizeYAML(YAML.stringify(deploymentConfSCCBroker, Infinity, 2));
fs.writeFileSync(kubeConfSCCBroker, formattedYAMLStringSCCBroker);
let ingressKubeFileName = 'scc-ingress.yaml';
let sccWorkerDeploymentFileName = 'scc-worker-deployment.yaml';
let deploySuccess = () => {
successMessage(
`The '${appName}' app was deployed successfully - You should be able to access it online ` +
`once it has finished booting up. This can take a while depending on your platform.`
);
process.exit();
};
if (isUpdate) {
try {
execSync(`kubectl replace -f ${kubernetesDirPath}/${sccWorkerDeploymentFileName}`, {stdio: 'inherit'});
} catch (err) {}
deploySuccess();
} else {
let kubeFiles = fs.readdirSync(kubernetesDirPath);
let serviceAndDeploymentKubeFiles = kubeFiles.filter((configFilePath) => {
return configFilePath != ingressKubeFileName;
});
serviceAndDeploymentKubeFiles.forEach((configFilePath) => {
let absolutePath = path.resolve(kubernetesDirPath, configFilePath);
execSync(`kubectl create -f ${absolutePath}`, {stdio: 'inherit'});
});
// Wait a few seconds before deploying ingress (due to a bug in some environments).
setTimeout(() => {
try {
execSync(`kubectl create -f ${kubernetesDirPath}/${ingressKubeFileName}`, {stdio: 'inherit'});
deploySuccess();
} catch (err) {
failedToDeploy(err);
}
}, 7000);
}
} catch (err) {
failedToDeploy(err);
}
};
let handleDockerVersionTagAndPushToDockerImageRepo = function (versionTag) {
socketClusterK8sConfig.docker.imageName = setImageVersionTag(socketClusterK8sConfig.docker.imageName, nextVersionTag);
let dockerConfig = socketClusterK8sConfig.docker;
if (dockerConfig.auth) {
let authParts = Buffer.from(dockerConfig.auth, 'base64').toString('utf8').split(':');
dockerUsername = authParts[0];
dockerPassword = authParts[1];
performDeployment(dockerConfig, versionTag, dockerUsername, dockerPassword);
} else {
promptDockerAuthDetails((username, password) => {
performDeployment(dockerConfig, versionTag, username, password);
});
}
};
let incrementVersion = function (versionString) {
return versionString.replace(/[^.]$/, (match) => {
return parseInt(match) + 1;
});
};
let pushToDockerImageRepo = function () {
let versionTagString = parseVersionTag(socketClusterK8sConfig.docker.imageName).replace(/^:/, '');
if (versionTagString) {
if (isUpdate) {
nextVersionTag = incrementVersion(versionTagString);
} else {
nextVersionTag = versionTagString;
}
} else {
nextVersionTag = dockerDefaultImageVersionTag;
}
promptInput(`Enter the Docker version tag for this deployment (Default: ${nextVersionTag}):`, handleDockerVersionTagAndPushToDockerImageRepo);
};
if (socketClusterK8sConfig.docker && socketClusterK8sConfig.docker.imageRepo) {
pushToDockerImageRepo();
} else {
let saveSocketClusterK8sConfigs = function () {
socketClusterK8sConfig.docker = {
imageRepo: 'https://index.docker.io/v1/',
imageName: dockerImageName
};
if (saveDockerAuthDetails) {
addAuthDetailsToSocketClusterK8s(socketClusterK8sConfig, dockerUsername, dockerPassword);
}
try {
saveSocketClusterK8sConfigFile(socketClusterK8sConfig);
} catch (err) {
failedToDeploy(err);
}
pushToDockerImageRepo();
};
let handleDockerImageName = function (imageName) {
imageName = imageName || dockerDefaultImageName;
let slashes = imageName.match(/\//g) || [];
if (slashes.length !== 1) {
failedToDeploy(
new Error('Invalid Docker image name; it must be in the format organizationName/projectName')
);
}
dockerImageName = setImageVersionTag(imageName, dockerDefaultImageVersionTag);
saveSocketClusterK8sConfigs();
};
let promptDockerImageName = function () {
dockerDefaultImageName = `${dockerUsername}/${appName}`;
promptInput(`Enter the Docker image name without the version tag (Or press enter for default: ${dockerDefaultImageName}):`, handleDockerImageName);
};
promptK8sTLSCredentials(() => {
promptDockerAuthDetails(promptDockerImageName);
});
}
to see what steps are executed for the deploy. It's not very complex but the logic here looks messy because it was written a long time ago before async/await (you kind of have to read the code backwards to follow the callbacks ;p).

2. Build a custom scc-worker from scratch with your source code built in

It will still work the same way architecturally but in this case it takes more space since the entire Node.js environment is inside the container (not just your source code) and it has to be entirely rebuilt and pushed even if you do a 1-line change.
That said this should be a lot easier to do manually.

In this case, you can simply customize scc-worker-deployment.yaml to point to your container image and make sure that you use the same environment variables so that your containers will be exposed correctly to the cluster.

@wormszer
Copy link

wormszer commented Feb 7, 2021

I have the same issue. I am running on windows. If i paste the command in powershell it will run.
It seems like maybe its something in the execSync function with the chained commands that may have changed.

@wormszer
Copy link

wormszer commented Feb 7, 2021

seems like this fix to the cli.js seems to work. basically splitting up the login and push.

  execSync(`docker build -t ${dockerConfig.imageName} .`, {stdio: 'inherit'});
  execSync(`${dockerLoginCommand}`, {stdio: 'inherit'});
  execSync(`docker push ${dockerConfig.imageName}`, {stdio: 'inherit'});

@hervejegou
Copy link
Author

@jondubois We have moved to a deployment using GitHub actions. So every PR triggers a test image build and if it passes, the PR can be merged and on merge (or on any direct commit) we have a GitHub action that deploys the update to our K8 cluster.
If that is of interest, I can ask our developer to write a quick guide on that.
So we are all good for now. Thanks.

@jondubois
Copy link
Member

@hervejegou Glad you found a solution. A guide would be great. If you share the link with me, I can add it to the socketcluster.io website.

We have a pending PR which should address the issue with the CLI. I just haven't had the time to test it yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants