Reputation: 739
I try to export all stacks from a remote portainer.io server with C# but I get definitely more stacks then I can see on the UI?
Let's say for an endpoint there are 10 stacks listed in the UI, but I get over 20 exported for the same endpoint.
I have nothing filtered on the UI and the credentials that I use in my script is the same as I log in manually to the UI.
In the same code I am also listing all the stacks according to the endpoints (containers). Listing results as expected. Only that stacks are listed that I can also see in the UI.
What could be the problem? Is it possible that I export additionally stacks that are not active? If yes How can I export just the active one?
static async Task Main(string[] args)
{
string baseUrl = "https://myserver/portainer";
string username = "myuser";
string password = "mypassword";
string exportPath = @"C:\temp\Portainer\Export"; for export
string stacksFilePath = @"C:\temp\Portainer\Export\stacks.txt";
try
{
// Authenticate and get token
string token = await Authenticate(baseUrl, username, password);
// List endpoints and stacks
await ListEndpointsAndStacks(baseUrl, token, stacksFilePath);
// Export all stacks
await ExportAllStacks(baseUrl, token, exportPath);
Console.WriteLine("All stacks exported successfully.");
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
Console.ReadKey();
}
}
private static async Task<string> Authenticate(string baseUrl, string username, string password)
{
using (var client = new HttpClient())
{
var authData = new { Username = username, Password = password };
var content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(authData));
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await client.PostAsync($"{baseUrl}/api/auth", content);
response.EnsureSuccessStatusCode();
var responseData = await response.Content.ReadAsStringAsync();
return JObject.Parse(responseData)["jwt"].ToString(); // Extract JWT token
}
}
private static async Task ListEndpointsAndStacks(string baseUrl, string token, string filePath)
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var sb = new StringBuilder();
// Fetch all endpoints
var endpointsResponse = await client.GetAsync($"{baseUrl}/api/endpoints");
endpointsResponse.EnsureSuccessStatusCode();
var endpoints = JArray.Parse(await endpointsResponse.Content.ReadAsStringAsync());
// Fetch all stacks globally
var stacksResponse = await client.GetAsync($"{baseUrl}/api/stacks");
stacksResponse.EnsureSuccessStatusCode();
var stacks = JArray.Parse(await stacksResponse.Content.ReadAsStringAsync());
sb.AppendLine("Endpoints and Stacks:");
sb.AppendLine("=====================");
foreach (var endpoint in endpoints)
{
string endpointId = endpoint["Id"].ToString();
string endpointName = endpoint["Name"].ToString();
sb.AppendLine($"Endpoint: {endpointName} (ID: {endpointId})");
// Filter stacks for this endpoint
var stacksForEndpoint = stacks.Where(stack => stack["EndpointId"].ToString() == endpointId);
if (!stacksForEndpoint.Any())
{
sb.AppendLine(" No stacks found.");
}
else
{
foreach (var stack in stacksForEndpoint)
{
string stackName = stack["Name"].ToString();
sb.AppendLine($" Stack: {stackName}");
}
}
sb.AppendLine(); // Blank line between endpoints
}
// Write all data to file
File.WriteAllText(filePath, sb.ToString());
Console.WriteLine($"Endpoints and stack names written to: {filePath}");
}
}
private static async Task ExportAllStacks(string baseUrl, string token, string exportPath)
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
// Ensure the export directory exists
Directory.CreateDirectory(exportPath);
// Fetch all endpoints
var endpointsResponse = await client.GetAsync($"{baseUrl}/api/endpoints");
endpointsResponse.EnsureSuccessStatusCode();
var endpoints = JArray.Parse(await endpointsResponse.Content.ReadAsStringAsync());
foreach (var endpoint in endpoints)
{
string endpointId = endpoint["Id"].ToString();
string endpointName = endpoint["Name"].ToString();
Console.WriteLine($"Processing Endpoint: {endpointName} (ID: {endpointId})");
// Fetch all stacks in this endpoint
var stacksResponse = await client.GetAsync($"{baseUrl}/api/stacks");
stacksResponse.EnsureSuccessStatusCode();
var stacks = JArray.Parse(await stacksResponse.Content.ReadAsStringAsync());
foreach (var stack in stacks)
{
string stackId = stack["Id"].ToString();
string stackName = stack["Name"].ToString();
try
{
Console.WriteLine($"Exporting Stack: {stackName} (ID: {stackId})");
// Fetch stack file content
var stackFileResponse = await client.GetAsync($"{baseUrl}/api/stacks/{stackId}/file");
if (!stackFileResponse.IsSuccessStatusCode)
{
Console.WriteLine($" Failed to export stack '{stackName}': {stackFileResponse.ReasonPhrase}");
continue; // Skip this stack and continue with the next one
}
string stackFileContent = await stackFileResponse.Content.ReadAsStringAsync();
// Save to a file
string sanitizedFileName = Path.Combine(exportPath, $"{endpointName}_{stackName.Replace(" ", "_")}.txt");
File.WriteAllText(sanitizedFileName, stackFileContent);
Console.WriteLine($" Stack '{stackName}' exported to '{sanitizedFileName}'");
}
catch (Exception ex)
{
Console.WriteLine($" Error exporting stack '{stackName}': {ex.Message}");
}
}
}
}
}
My code:
Upvotes: 0
Views: 21