Skip to content

Commit

Permalink
Merge pull request #3 from DotNetNomads/create-secrets-and-configs
Browse files Browse the repository at this point in the history
WIP: Secrets, Configs: cmd
  • Loading branch information
binali-rustamov authored Nov 20, 2023
2 parents 8d52482 + d0b9ab7 commit d093aeb
Show file tree
Hide file tree
Showing 29 changed files with 765 additions and 61 deletions.
21 changes: 21 additions & 0 deletions PortainerClient/Api/Base/BaseApiService.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text.Json;
using PortainerClient.Api.Model;
using PortainerClient.Config;
Expand Down Expand Up @@ -126,5 +128,24 @@ private static InvalidOperationException ParseError(string resource, RestRespons
return new InvalidOperationException(
$"Request {resource}: {(errorInfo != null ? $"{errorInfo.message}, details: {errorInfo.details}" : "no information")}");
}

/// <summary>
/// Set default ACLs to resource
/// </summary>
/// <param name="memberships"></param>
/// <param name="debug"></param>
/// <param name="resourceId"></param>
protected void SetAcl(IEnumerable<Membership> memberships, bool debug, int resourceId)
{
var resourceControlRequest = new ResourceControlRequest
{
Public = false,
AdministratorsOnly = false,
Users = Array.Empty<int>(),
Teams = memberships.Select(m => m.TeamId).ToArray(),
};
Put<ResourceControl>($"resource_controls/{resourceId}", debug,
("", resourceControlRequest, ParamType.JsonBody));
}
}
}
83 changes: 83 additions & 0 deletions PortainerClient/Api/EndpointsApiService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PortainerClient.Api.Base;
using PortainerClient.Api.Model;

namespace PortainerClient.Api;

/// <summary>
/// API implementation for endpoints API
/// </summary>
public class EndpointsApiService : BaseApiService
{
/// <summary>
/// Get all configs
/// </summary>
/// <param name="endpointId"></param>
/// <returns>List of available configs</returns>
public IEnumerable<DockerDto> GetConfigs(int endpointId) =>
Get<List<DockerDto>>($"endpoints/{endpointId}/docker/configs");

/// <summary>
/// Get all secrets
/// </summary>
/// <param name="endpointId"></param>
/// <returns>List of available secrets</returns>
public IEnumerable<DockerDto> GetSecrets(int endpointId) =>
Get<List<DockerDto>>($"endpoints/{endpointId}/docker/secrets");

/// <summary>
/// Get list of available endpoints
/// </summary>
/// <returns>List of endpoints</returns>
public IEnumerable<Endpoint> GetEndpoints() =>
Get<List<Endpoint>>("endpoints", false, ("type", 2)).Select(e =>
{
e.SwarmId = Get<EndpointSwarm>($"endpoints/{e.Id}/docker/swarm").Id;
return e;
}
);

/// <summary>
/// Create new secret in specific Swarm endpoint
/// </summary>
/// <param name="secretName"></param>
/// <param name="fileContent"></param>
/// <param name="memberships"></param>
/// <param name="endpoint"></param>
/// <param name="debug"></param>
public void CreateSecret(string secretName, string fileContent, IEnumerable<Membership>? memberships,
Endpoint endpoint, bool debug = false)
{
var secretSpec = new ConfigSpec
{
Name = secretName,
Data = Convert.ToBase64String(Encoding.UTF8.GetBytes(fileContent))
};
var secret = Post<DockerDto>($"endpoints/{endpoint.Id}/docker/secrets/create", debug,
("", secretSpec, ParamType.JsonBody));
SetAcl(memberships, debug, secret.Portainer.ResourceControl.Id);
}

/// <summary>
/// Create new config in specific Swarm endpoint
/// </summary>
/// <param name="configName"></param>
/// <param name="fileContent"></param>
/// <param name="memberships"></param>
/// <param name="endpoint"></param>
/// <param name="debug"></param>
public void CreateConfig(string configName, string fileContent, IEnumerable<Membership>? memberships, Endpoint endpoint, bool debug)
{
var configSec = new ConfigSpec
{
Name = configName,
Data = Convert.ToBase64String(Encoding.UTF8.GetBytes(fileContent))
};
var secret = Post<DockerDto>($"endpoints/{endpoint.Id}/docker/configs/create", debug,
("", configSec, ParamType.JsonBody));
SetAcl(memberships, debug, secret.Portainer.ResourceControl.Id);
}
}
13 changes: 13 additions & 0 deletions PortainerClient/Api/Model/ConfigSpec.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System.Collections.Generic;

namespace PortainerClient.Api.Model;

/// <summary>
/// Spec of Docker Swarm Config instance
/// </summary>
public class ConfigSpec
{
public string Data { get; set; }
public Dictionary<string, object> Labels { get; set; }
public string Name { get; set; }
}
16 changes: 16 additions & 0 deletions PortainerClient/Api/Model/DockerDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;

namespace PortainerClient.Api.Model;

/// <summary>
/// Docker specific instance info
/// </summary>
public class DockerDto
{
public DateTime CreatedAt { get; set; }
public string ID { get; set; }
public Portainer Portainer { get; set; }
public ConfigSpec Spec { get; set; }
public DateTime UpdatedAt { get; set; }
public Version Version { get; set; }
}
21 changes: 21 additions & 0 deletions PortainerClient/Api/Model/Endpoint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace PortainerClient.Api.Model;

/// <summary>
/// Portainer Endpoint
/// </summary>
public class Endpoint
{
/// <summary>
/// Identifier
/// </summary>
public int Id { get; set; }
/// <summary>
/// Name in portainer's interface
/// </summary>
public string Name { get; set; } = null!;

/// <summary>
/// Identifier of swarm cluster
/// </summary>
public string SwarmId { get; set; } = null!;
}
12 changes: 12 additions & 0 deletions PortainerClient/Api/Model/EndpointSwarm.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace PortainerClient.Api.Model;

/// <summary>
/// Swarm info for endpoint
/// </summary>
public class EndpointSwarm
{
/// <summary>
/// Swarm identifier
/// </summary>
public string Id { get; set; } = null!;
}
20 changes: 20 additions & 0 deletions PortainerClient/Api/Model/Membership.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace PortainerClient.Api.Model;

/// <summary>
/// Membership entity
/// </summary>
public class Membership
{
/// <summary>
/// Team ID
/// </summary>
public int TeamId { get; set; }
/// <summary>
/// Role in team
/// </summary>
public int Role { get; set; }
/// <summary>
/// Team name
/// </summary>
public string TeamName { get; set; } = null!;
}
12 changes: 12 additions & 0 deletions PortainerClient/Api/Model/Portainer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace PortainerClient.Api.Model;

/// <summary>
/// Represents info about Docker Resource in Portainer
/// </summary>
public class Portainer
{
/// <summary>
/// Resource control information from portainer
/// </summary>
public ResourceControl ResourceControl { get; set; }
}
26 changes: 26 additions & 0 deletions PortainerClient/Api/Model/ResourceControlRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System.Collections.Generic;

namespace PortainerClient.Api.Model;

/// <summary>
/// Model to change resource control settings
/// </summary>
public class ResourceControlRequest
{
/// <summary>
/// Allow only for admins
/// </summary>
public bool AdministratorsOnly { get; set; }
/// <summary>
/// Accessible for any user
/// </summary>
public bool Public { get; set; }
/// <summary>
/// Users have access
/// </summary>
public IEnumerable<int> Users { get; set; }
/// <summary>
/// Teams have access
/// </summary>
public IEnumerable<int> Teams { get; set; }
}
16 changes: 16 additions & 0 deletions PortainerClient/Api/Model/Team.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace PortainerClient.Api.Model;

/// <summary>
/// Team info
/// </summary>
public class Team
{
/// <summary>
/// Identifier of Team
/// </summary>
public int Id { get; set; }
/// <summary>
/// Team name
/// </summary>
public string Name { get; set; } = null!;
}
9 changes: 9 additions & 0 deletions PortainerClient/Api/Model/Version.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace PortainerClient.Api.Model;

/// <summary>
/// Version of object
/// </summary>
public class Version
{
public int Index { get; set; }
}
27 changes: 27 additions & 0 deletions PortainerClient/Api/PortainerApiService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.Collections.Generic;
using System.Linq;
using PortainerClient.Api.Base;
using PortainerClient.Api.Model;

namespace PortainerClient.Api;

/// <summary>
/// Different APIs of Portainer
/// </summary>
public class PortainerApiService : BaseApiService
{
/// <summary>
/// Get memberships of user
/// </summary>
/// <param name="userId">User identifier</param>
/// <returns>List of memberships</returns>
public IEnumerable<Membership> GetMemberships(int userId)
{
var teams = Get<List<Team>>("teams");
return Get<List<Membership>>($"users/{userId}/memberships").Select(m =>
{
m.TeamName = teams.First(t => t.Id == m.TeamId).Name;
return m;
});
}
}
28 changes: 19 additions & 9 deletions PortainerClient/Api/StacksApiService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Text.Json;
using PortainerClient.Api.Base;
using PortainerClient.Api.Model;
using PortainerClient.Config;
using RestSharp;

namespace PortainerClient.Api
Expand Down Expand Up @@ -46,20 +47,29 @@ public IEnumerable<StackInfo> GetStacks(bool debug = false) =>
/// <param name="endpointId">Endpoint identifier where to deploy a stack</param>
/// <param name="name">The stack name</param>
/// <param name="swarmId">Swarm identifier where to deploy a stack</param>
/// <param name="stackFilePath">Path to stack deployment file</param>
/// <param name="fileContent">File content of stack</param>
/// <param name="env">List of the stack envs</param>
/// <param name="memberships"></param>
/// <param name="debug">Print content of request and response</param>
/// <returns>StackInfo instance for newly deployed stack</returns>
public StackInfo DeployStack(int endpointId, string name, string swarmId, string stackFilePath,
List<Env> env, bool debug = false) =>
Post<StackInfo>("stacks/create/swarm/file",
public StackInfo DeployStack(int endpointId, string name, string swarmId, string fileContent,
List<Env> env, IEnumerable<Membership> memberships, bool debug = false)
{
var stack = Post<StackInfo>($"stacks/create/swarm/string",
debug,
("endpointId", endpointId, ParamType.QueryParam),
("file", stackFilePath, ParamType.File),
("SwarmID", swarmId, ParamType.BodyParam),
("Name", name, ParamType.BodyParam),
("Env", JsonSerializer.Serialize(env), ParamType.BodyParam)
);
(null, new
{
StackFileContent = fileContent,
Env = env,
method = "string",
type = "swarm",
SwarmID = swarmId,
Name = name
}, ParamType.JsonBody));
SetAcl(memberships, debug, stack.ResourceControl.Id);
return stack;
}

/// <summary>
/// Update a stack
Expand Down
14 changes: 14 additions & 0 deletions PortainerClient/Command/Auth/AuthCmd.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.IdentityModel.Tokens.Jwt;
using System.Net;
using McMaster.Extensions.CommandLineUtils;
using PortainerClient.Api;
using PortainerClient.Api.Model;
using PortainerClient.Config;
using RestSharp;
Expand Down Expand Up @@ -63,6 +65,18 @@ private static void Authorize(string url, string user, string password)
Token = tokenInfoResponse.Data.Jwt
};
configModel.Save();

var handler = new JwtSecurityTokenHandler();
var jwtSecurityToken = handler.ReadJwtToken(tokenInfoResponse.Data.Jwt);
var userId = jwtSecurityToken.Payload["id"].ToString();

var workspace = new WorkspaceInfoModel
{
UserId = int.Parse(userId)
};
workspace.Memberships = new PortainerApiService().GetMemberships(workspace.UserId);
workspace.Endpoints = new EndpointsApiService().GetEndpoints();
workspace.Save();
}

/// <inheritdoc />
Expand Down
15 changes: 15 additions & 0 deletions PortainerClient/Command/Configs/ConfigsCmd.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using McMaster.Extensions.CommandLineUtils;
using PortainerClient.Helpers;

namespace PortainerClient.Command.Configs;

/// <summary>
/// CMD command for Configs
/// </summary>
[Command(Name = "configs", Description = "Docker Swarm Configs management commands")]
[Subcommand(typeof(ConfigsLsCmd), typeof(ConfigsCreateCmd))]
public class ConfigsCmd : ICommand
{
/// <inheritdoc />
public int OnExecute(CommandLineApplication app, IConsole console) => CmdHelpers.SpecifyCommandResult(app, console);
}
Loading

0 comments on commit d093aeb

Please sign in to comment.