mirror of https://github.com/portainer/portainer
Merge branch 'develop' into webpack
commit
5f4c42393d
|
@ -142,6 +142,7 @@ func (store *Store) MigrateData() error {
|
||||||
ResourceControlService: store.ResourceControlService,
|
ResourceControlService: store.ResourceControlService,
|
||||||
SettingsService: store.SettingsService,
|
SettingsService: store.SettingsService,
|
||||||
StackService: store.StackService,
|
StackService: store.StackService,
|
||||||
|
TemplateService: store.TemplateService,
|
||||||
UserService: store.UserService,
|
UserService: store.UserService,
|
||||||
VersionService: store.VersionService,
|
VersionService: store.VersionService,
|
||||||
FileService: store.fileService,
|
FileService: store.fileService,
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
package migrator
|
package migrator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/portainer/portainer"
|
||||||
|
)
|
||||||
|
|
||||||
func (m *Migrator) updateSettingsToDBVersion15() error {
|
func (m *Migrator) updateSettingsToDBVersion15() error {
|
||||||
legacySettings, err := m.settingsService.Settings()
|
legacySettings, err := m.settingsService.Settings()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -9,3 +15,21 @@ func (m *Migrator) updateSettingsToDBVersion15() error {
|
||||||
legacySettings.EnableHostManagementFeatures = false
|
legacySettings.EnableHostManagementFeatures = false
|
||||||
return m.settingsService.UpdateSettings(legacySettings)
|
return m.settingsService.UpdateSettings(legacySettings)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Migrator) updateTemplatesToVersion15() error {
|
||||||
|
legacyTemplates, err := m.templateService.Templates()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, template := range legacyTemplates {
|
||||||
|
template.Logo = strings.Replace(template.Logo, "https://portainer.io/images", portainer.AssetsServerURL, -1)
|
||||||
|
|
||||||
|
err = m.templateService.UpdateTemplate(template.ID, &template)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
package migrator
|
||||||
|
|
||||||
|
func (m *Migrator) updateSettingsToDBVersion16() error {
|
||||||
|
legacySettings, err := m.settingsService.Settings()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if legacySettings.SnapshotInterval == "" {
|
||||||
|
legacySettings.SnapshotInterval = "5m"
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.settingsService.UpdateSettings(legacySettings)
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/portainer/portainer/bolt/resourcecontrol"
|
"github.com/portainer/portainer/bolt/resourcecontrol"
|
||||||
"github.com/portainer/portainer/bolt/settings"
|
"github.com/portainer/portainer/bolt/settings"
|
||||||
"github.com/portainer/portainer/bolt/stack"
|
"github.com/portainer/portainer/bolt/stack"
|
||||||
|
"github.com/portainer/portainer/bolt/template"
|
||||||
"github.com/portainer/portainer/bolt/user"
|
"github.com/portainer/portainer/bolt/user"
|
||||||
"github.com/portainer/portainer/bolt/version"
|
"github.com/portainer/portainer/bolt/version"
|
||||||
)
|
)
|
||||||
|
@ -22,6 +23,7 @@ type (
|
||||||
resourceControlService *resourcecontrol.Service
|
resourceControlService *resourcecontrol.Service
|
||||||
settingsService *settings.Service
|
settingsService *settings.Service
|
||||||
stackService *stack.Service
|
stackService *stack.Service
|
||||||
|
templateService *template.Service
|
||||||
userService *user.Service
|
userService *user.Service
|
||||||
versionService *version.Service
|
versionService *version.Service
|
||||||
fileService portainer.FileService
|
fileService portainer.FileService
|
||||||
|
@ -36,6 +38,7 @@ type (
|
||||||
ResourceControlService *resourcecontrol.Service
|
ResourceControlService *resourcecontrol.Service
|
||||||
SettingsService *settings.Service
|
SettingsService *settings.Service
|
||||||
StackService *stack.Service
|
StackService *stack.Service
|
||||||
|
TemplateService *template.Service
|
||||||
UserService *user.Service
|
UserService *user.Service
|
||||||
VersionService *version.Service
|
VersionService *version.Service
|
||||||
FileService portainer.FileService
|
FileService portainer.FileService
|
||||||
|
@ -51,6 +54,7 @@ func NewMigrator(parameters *Parameters) *Migrator {
|
||||||
endpointService: parameters.EndpointService,
|
endpointService: parameters.EndpointService,
|
||||||
resourceControlService: parameters.ResourceControlService,
|
resourceControlService: parameters.ResourceControlService,
|
||||||
settingsService: parameters.SettingsService,
|
settingsService: parameters.SettingsService,
|
||||||
|
templateService: parameters.TemplateService,
|
||||||
stackService: parameters.StackService,
|
stackService: parameters.StackService,
|
||||||
userService: parameters.UserService,
|
userService: parameters.UserService,
|
||||||
versionService: parameters.VersionService,
|
versionService: parameters.VersionService,
|
||||||
|
@ -186,12 +190,24 @@ func (m *Migrator) Migrate() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Portainer 1.20-dev
|
// Portainer 1.20.0
|
||||||
if m.currentDBVersion < 15 {
|
if m.currentDBVersion < 15 {
|
||||||
err := m.updateSettingsToDBVersion15()
|
err := m.updateSettingsToDBVersion15()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = m.updateTemplatesToVersion15()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.currentDBVersion < 16 {
|
||||||
|
err := m.updateSettingsToDBVersion16()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return m.versionService.StoreDBVersion(portainer.DBVersion)
|
return m.versionService.StoreDBVersion(portainer.DBVersion)
|
||||||
|
|
|
@ -116,9 +116,10 @@ func initJobScheduler() portainer.JobScheduler {
|
||||||
return cron.NewJobScheduler()
|
return cron.NewJobScheduler()
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadSnapshotSystemSchedule(jobScheduler portainer.JobScheduler, snapshotter portainer.Snapshotter, scheduleService portainer.ScheduleService, endpointService portainer.EndpointService, flags *portainer.CLIFlags) error {
|
func loadSnapshotSystemSchedule(jobScheduler portainer.JobScheduler, snapshotter portainer.Snapshotter, scheduleService portainer.ScheduleService, endpointService portainer.EndpointService, settingsService portainer.SettingsService) error {
|
||||||
if !*flags.Snapshot {
|
settings, err := settingsService.Settings()
|
||||||
return nil
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
schedules, err := scheduleService.SchedulesByJobType(portainer.SnapshotJobType)
|
schedules, err := scheduleService.SchedulesByJobType(portainer.SnapshotJobType)
|
||||||
|
@ -126,20 +127,20 @@ func loadSnapshotSystemSchedule(jobScheduler portainer.JobScheduler, snapshotter
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(schedules) != 0 {
|
var snapshotSchedule *portainer.Schedule
|
||||||
return nil
|
if len(schedules) == 0 {
|
||||||
}
|
snapshotJob := &portainer.SnapshotJob{}
|
||||||
|
snapshotSchedule = &portainer.Schedule{
|
||||||
snapshotJob := &portainer.SnapshotJob{}
|
ID: portainer.ScheduleID(scheduleService.GetNextIdentifier()),
|
||||||
|
Name: "system_snapshot",
|
||||||
snapshotSchedule := &portainer.Schedule{
|
CronExpression: "@every " + settings.SnapshotInterval,
|
||||||
ID: portainer.ScheduleID(scheduleService.GetNextIdentifier()),
|
Recurring: true,
|
||||||
Name: "system_snapshot",
|
JobType: portainer.SnapshotJobType,
|
||||||
CronExpression: "@every " + *flags.SnapshotInterval,
|
SnapshotJob: snapshotJob,
|
||||||
Recurring: true,
|
Created: time.Now().Unix(),
|
||||||
JobType: portainer.SnapshotJobType,
|
}
|
||||||
SnapshotJob: snapshotJob,
|
} else {
|
||||||
Created: time.Now().Unix(),
|
snapshotSchedule = &schedules[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
snapshotJobContext := cron.NewSnapshotJobContext(endpointService, snapshotter)
|
snapshotJobContext := cron.NewSnapshotJobContext(endpointService, snapshotter)
|
||||||
|
@ -150,7 +151,10 @@ func loadSnapshotSystemSchedule(jobScheduler portainer.JobScheduler, snapshotter
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return scheduleService.CreateSchedule(snapshotSchedule)
|
if len(schedules) == 0 {
|
||||||
|
return scheduleService.CreateSchedule(snapshotSchedule)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadEndpointSyncSystemSchedule(jobScheduler portainer.JobScheduler, scheduleService portainer.ScheduleService, endpointService portainer.EndpointService, flags *portainer.CLIFlags) error {
|
func loadEndpointSyncSystemSchedule(jobScheduler portainer.JobScheduler, scheduleService portainer.ScheduleService, endpointService portainer.EndpointService, flags *portainer.CLIFlags) error {
|
||||||
|
@ -538,25 +542,6 @@ func main() {
|
||||||
|
|
||||||
snapshotter := initSnapshotter(clientFactory)
|
snapshotter := initSnapshotter(clientFactory)
|
||||||
|
|
||||||
jobScheduler := initJobScheduler()
|
|
||||||
|
|
||||||
err = loadSchedulesFromDatabase(jobScheduler, jobService, store.ScheduleService, store.EndpointService, fileService)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = loadEndpointSyncSystemSchedule(jobScheduler, store.ScheduleService, store.EndpointService, flags)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = loadSnapshotSystemSchedule(jobScheduler, snapshotter, store.ScheduleService, store.EndpointService, flags)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
jobScheduler.Start()
|
|
||||||
|
|
||||||
endpointManagement := true
|
endpointManagement := true
|
||||||
if *flags.ExternalEndpoints != "" {
|
if *flags.ExternalEndpoints != "" {
|
||||||
endpointManagement = false
|
endpointManagement = false
|
||||||
|
@ -579,6 +564,27 @@ func main() {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jobScheduler := initJobScheduler()
|
||||||
|
|
||||||
|
err = loadSchedulesFromDatabase(jobScheduler, jobService, store.ScheduleService, store.EndpointService, fileService)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = loadEndpointSyncSystemSchedule(jobScheduler, store.ScheduleService, store.EndpointService, flags)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if *flags.Snapshot {
|
||||||
|
err = loadSnapshotSystemSchedule(jobScheduler, snapshotter, store.ScheduleService, store.EndpointService, store.SettingsService)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jobScheduler.Start()
|
||||||
|
|
||||||
err = initDockerHub(store.DockerHubService)
|
err = initDockerHub(store.DockerHubService)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
|
|
@ -45,39 +45,41 @@ func (runner *SnapshotJobRunner) GetSchedule() *portainer.Schedule {
|
||||||
// As a snapshot can be a long process, to avoid any concurrency issue we
|
// As a snapshot can be a long process, to avoid any concurrency issue we
|
||||||
// retrieve the latest version of the endpoint right after a snapshot.
|
// retrieve the latest version of the endpoint right after a snapshot.
|
||||||
func (runner *SnapshotJobRunner) Run() {
|
func (runner *SnapshotJobRunner) Run() {
|
||||||
endpoints, err := runner.context.endpointService.Endpoints()
|
go func() {
|
||||||
if err != nil {
|
endpoints, err := runner.context.endpointService.Endpoints()
|
||||||
log.Printf("background schedule error (endpoint snapshot). Unable to retrieve endpoint list (err=%s)\n", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, endpoint := range endpoints {
|
|
||||||
if endpoint.Type == portainer.AzureEnvironment {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
snapshot, snapshotError := runner.context.snapshotter.CreateSnapshot(&endpoint)
|
|
||||||
|
|
||||||
latestEndpointReference, err := runner.context.endpointService.Endpoint(endpoint.ID)
|
|
||||||
if latestEndpointReference == nil {
|
|
||||||
log.Printf("background schedule error (endpoint snapshot). Endpoint not found inside the database anymore (endpoint=%s, URL=%s) (err=%s)\n", endpoint.Name, endpoint.URL, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
latestEndpointReference.Status = portainer.EndpointStatusUp
|
|
||||||
if snapshotError != nil {
|
|
||||||
log.Printf("background schedule error (endpoint snapshot). Unable to create snapshot (endpoint=%s, URL=%s) (err=%s)\n", endpoint.Name, endpoint.URL, snapshotError)
|
|
||||||
latestEndpointReference.Status = portainer.EndpointStatusDown
|
|
||||||
}
|
|
||||||
|
|
||||||
if snapshot != nil {
|
|
||||||
latestEndpointReference.Snapshots = []portainer.Snapshot{*snapshot}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = runner.context.endpointService.UpdateEndpoint(latestEndpointReference.ID, latestEndpointReference)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("background schedule error (endpoint snapshot). Unable to update endpoint (endpoint=%s, URL=%s) (err=%s)\n", endpoint.Name, endpoint.URL, err)
|
log.Printf("background schedule error (endpoint snapshot). Unable to retrieve endpoint list (err=%s)\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
for _, endpoint := range endpoints {
|
||||||
|
if endpoint.Type == portainer.AzureEnvironment {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
snapshot, snapshotError := runner.context.snapshotter.CreateSnapshot(&endpoint)
|
||||||
|
|
||||||
|
latestEndpointReference, err := runner.context.endpointService.Endpoint(endpoint.ID)
|
||||||
|
if latestEndpointReference == nil {
|
||||||
|
log.Printf("background schedule error (endpoint snapshot). Endpoint not found inside the database anymore (endpoint=%s, URL=%s) (err=%s)\n", endpoint.Name, endpoint.URL, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
latestEndpointReference.Status = portainer.EndpointStatusUp
|
||||||
|
if snapshotError != nil {
|
||||||
|
log.Printf("background schedule error (endpoint snapshot). Unable to create snapshot (endpoint=%s, URL=%s) (err=%s)\n", endpoint.Name, endpoint.URL, snapshotError)
|
||||||
|
latestEndpointReference.Status = portainer.EndpointStatusDown
|
||||||
|
}
|
||||||
|
|
||||||
|
if snapshot != nil {
|
||||||
|
latestEndpointReference.Snapshots = []portainer.Snapshot{*snapshot}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = runner.context.endpointService.UpdateEndpoint(latestEndpointReference.ID, latestEndpointReference)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("background schedule error (endpoint snapshot). Unable to update endpoint (endpoint=%s, URL=%s) (err=%s)\n", endpoint.Name, endpoint.URL, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ func (scheduler *JobScheduler) UpdateSystemJobSchedule(jobType portainer.JobType
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
newCron.Schedule(entry.Schedule, entry.Job)
|
newCron.Schedule(entry.Schedule, entry.Job)
|
||||||
|
@ -72,6 +73,7 @@ func (scheduler *JobScheduler) UpdateJobSchedule(runner portainer.JobRunner) err
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
newCron.Schedule(entry.Schedule, entry.Job)
|
newCron.Schedule(entry.Schedule, entry.Job)
|
||||||
|
|
|
@ -8,8 +8,6 @@ import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/portainer/portainer"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -105,10 +103,10 @@ func (service *ECDSAService) GenerateKeyPair() ([]byte, []byte, error) {
|
||||||
// CreateSignature creates a digital signature.
|
// CreateSignature creates a digital signature.
|
||||||
// It automatically hash a specific message using MD5 and creates a signature from
|
// It automatically hash a specific message using MD5 and creates a signature from
|
||||||
// that hash.
|
// that hash.
|
||||||
|
// If a secret is associated to the service, it will be used instead of the specified
|
||||||
|
// message.
|
||||||
// It then encodes the generated signature in base64.
|
// It then encodes the generated signature in base64.
|
||||||
func (service *ECDSAService) CreateSignature() (string, error) {
|
func (service *ECDSAService) CreateSignature(message string) (string, error) {
|
||||||
|
|
||||||
message := portainer.PortainerAgentSignatureMessage
|
|
||||||
if service.secret != "" {
|
if service.secret != "" {
|
||||||
message = service.secret
|
message = service.secret
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package docker
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/docker/docker/client"
|
"github.com/docker/docker/client"
|
||||||
"github.com/portainer/portainer"
|
"github.com/portainer/portainer"
|
||||||
|
@ -67,7 +68,7 @@ func createAgentClient(endpoint *portainer.Endpoint, signatureService portainer.
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
signature, err := signatureService.CreateSignature()
|
signature, err := signatureService.CreateSignature(portainer.PortainerAgentSignatureMessage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -102,5 +103,6 @@ func httpClient(endpoint *portainer.Endpoint) (*http.Client, error) {
|
||||||
|
|
||||||
return &http.Client{
|
return &http.Client{
|
||||||
Transport: transport,
|
Transport: transport,
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,10 @@ func buildExtensionPath(binaryPath string, extension *portainer.Extension) strin
|
||||||
extensionFilename += "-" + runtime.GOOS + "-" + runtime.GOARCH
|
extensionFilename += "-" + runtime.GOOS + "-" + runtime.GOARCH
|
||||||
extensionFilename += "-" + extension.Version
|
extensionFilename += "-" + extension.Version
|
||||||
|
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
extensionFilename += ".exe"
|
||||||
|
}
|
||||||
|
|
||||||
extensionPath := path.Join(
|
extensionPath := path.Join(
|
||||||
binaryPath,
|
binaryPath,
|
||||||
extensionFilename)
|
extensionFilename)
|
||||||
|
|
|
@ -140,7 +140,7 @@ func (manager *SwarmStackManager) updateDockerCLIConfiguration(dataPath string)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
signature, err := manager.signatureService.CreateSignature()
|
signature, err := manager.signatureService.CreateSignature(portainer.PortainerAgentSignatureMessage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,10 @@ func (handler *Handler) extensionInspect(w http.ResponseWriter, r *http.Request)
|
||||||
for _, p := range extensions {
|
for _, p := range extensions {
|
||||||
if p.ID == extensionID {
|
if p.ID == extensionID {
|
||||||
extension = p
|
extension = p
|
||||||
|
if extension.DescriptionURL != "" {
|
||||||
|
description, _ := client.Get(extension.DescriptionURL, 10)
|
||||||
|
extension.Description = string(description)
|
||||||
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ func (handler *Handler) extensionList(w http.ResponseWriter, r *http.Request) *h
|
||||||
if storeDetails {
|
if storeDetails {
|
||||||
definitions, err := handler.ExtensionManager.FetchExtensionDefinitions()
|
definitions, err := handler.ExtensionManager.FetchExtensionDefinitions()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve extension definitions", err}
|
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to retrieve extensions", err}
|
||||||
}
|
}
|
||||||
|
|
||||||
for idx := range definitions {
|
for idx := range definitions {
|
||||||
|
|
|
@ -111,7 +111,7 @@ func (handler *Handler) proxyWebsocketRequest(w http.ResponseWriter, r *http.Req
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
signature, err := handler.SignatureService.CreateSignature()
|
signature, err := handler.SignatureService.CreateSignature(portainer.PortainerAgentSignatureMessage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ func (p *proxyTransport) proxyDockerRequest(request *http.Request) (*http.Respon
|
||||||
request.URL.Path = path
|
request.URL.Path = path
|
||||||
|
|
||||||
if p.enableSignature {
|
if p.enableSignature {
|
||||||
signature, err := p.SignatureService.CreateSignature()
|
signature, err := p.SignatureService.CreateSignature(portainer.PortainerAgentSignatureMessage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -485,6 +485,7 @@ type (
|
||||||
Name string `json:"Name,omitempty"`
|
Name string `json:"Name,omitempty"`
|
||||||
ShortDescription string `json:"ShortDescription,omitempty"`
|
ShortDescription string `json:"ShortDescription,omitempty"`
|
||||||
Description string `json:"Description,omitempty"`
|
Description string `json:"Description,omitempty"`
|
||||||
|
DescriptionURL string `json:"DescriptionURL,omitempty"`
|
||||||
Price string `json:"Price,omitempty"`
|
Price string `json:"Price,omitempty"`
|
||||||
PriceDescription string `json:"PriceDescription,omitempty"`
|
PriceDescription string `json:"PriceDescription,omitempty"`
|
||||||
Deal bool `json:"Deal,omitempty"`
|
Deal bool `json:"Deal,omitempty"`
|
||||||
|
@ -492,7 +493,7 @@ type (
|
||||||
License LicenseInformation `json:"License,omitempty"`
|
License LicenseInformation `json:"License,omitempty"`
|
||||||
Version string `json:"Version"`
|
Version string `json:"Version"`
|
||||||
UpdateAvailable bool `json:"UpdateAvailable"`
|
UpdateAvailable bool `json:"UpdateAvailable"`
|
||||||
ProductID int `json:"ProductId,omitempty"`
|
ShopURL string `json:"ShopURL,omitempty"`
|
||||||
Images []string `json:"Images,omitempty"`
|
Images []string `json:"Images,omitempty"`
|
||||||
Logo string `json:"Logo,omitempty"`
|
Logo string `json:"Logo,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -682,7 +683,7 @@ type (
|
||||||
GenerateKeyPair() ([]byte, []byte, error)
|
GenerateKeyPair() ([]byte, []byte, error)
|
||||||
EncodedPublicKey() string
|
EncodedPublicKey() string
|
||||||
PEMHeaders() (string, string)
|
PEMHeaders() (string, string)
|
||||||
CreateSignature() (string, error)
|
CreateSignature(message string) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// JWTService represents a service for managing JWT tokens
|
// JWTService represents a service for managing JWT tokens
|
||||||
|
@ -777,13 +778,15 @@ type (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// APIVersion is the version number of the Portainer API
|
// APIVersion is the version number of the Portainer API
|
||||||
APIVersion = "1.20-dev"
|
APIVersion = "1.20.0"
|
||||||
// DBVersion is the version number of the Portainer database
|
// DBVersion is the version number of the Portainer database
|
||||||
DBVersion = 15
|
DBVersion = 16
|
||||||
|
// AssetsServerURL represents the URL of the Portainer asset server
|
||||||
|
AssetsServerURL = "https://portainer-io-assets.sfo2.digitaloceanspaces.com"
|
||||||
// MessageOfTheDayURL represents the URL where Portainer MOTD message can be retrieved
|
// MessageOfTheDayURL represents the URL where Portainer MOTD message can be retrieved
|
||||||
MessageOfTheDayURL = "https://portainer-io-assets.sfo2.digitaloceanspaces.com/motd.html"
|
MessageOfTheDayURL = AssetsServerURL + "/motd.html"
|
||||||
// ExtensionDefinitionsURL represents the URL where Portainer extension definitions can be retrieved
|
// ExtensionDefinitionsURL represents the URL where Portainer extension definitions can be retrieved
|
||||||
ExtensionDefinitionsURL = "https://portainer-io-assets.sfo2.digitaloceanspaces.com/extensions.json"
|
ExtensionDefinitionsURL = AssetsServerURL + "/extensions.json"
|
||||||
// PortainerAgentHeader represents the name of the header available in any agent response
|
// PortainerAgentHeader represents the name of the header available in any agent response
|
||||||
PortainerAgentHeader = "Portainer-Agent"
|
PortainerAgentHeader = "Portainer-Agent"
|
||||||
// PortainerAgentTargetHeader represent the name of the header containing the target node name
|
// PortainerAgentTargetHeader represent the name of the header containing the target node name
|
||||||
|
|
|
@ -54,7 +54,7 @@ info:
|
||||||
|
|
||||||
**NOTE**: You can find more information on how to query the Docker API in the [Docker official documentation](https://docs.docker.com/engine/api/v1.30/) as well as in [this Portainer example](https://gist.github.com/deviantony/77026d402366b4b43fa5918d41bc42f8).
|
**NOTE**: You can find more information on how to query the Docker API in the [Docker official documentation](https://docs.docker.com/engine/api/v1.30/) as well as in [this Portainer example](https://gist.github.com/deviantony/77026d402366b4b43fa5918d41bc42f8).
|
||||||
|
|
||||||
version: "1.20-dev"
|
version: "1.20.0"
|
||||||
title: "Portainer API"
|
title: "Portainer API"
|
||||||
contact:
|
contact:
|
||||||
email: "info@portainer.io"
|
email: "info@portainer.io"
|
||||||
|
@ -3018,7 +3018,7 @@ definitions:
|
||||||
description: "Is analytics enabled"
|
description: "Is analytics enabled"
|
||||||
Version:
|
Version:
|
||||||
type: "string"
|
type: "string"
|
||||||
example: "1.20-dev"
|
example: "1.20.0"
|
||||||
description: "Portainer API version"
|
description: "Portainer API version"
|
||||||
PublicSettingsInspectResponse:
|
PublicSettingsInspectResponse:
|
||||||
type: "object"
|
type: "object"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"packageName": "portainer",
|
"packageName": "portainer",
|
||||||
"packageVersion": "1.20-dev",
|
"packageVersion": "1.20.0",
|
||||||
"projectName": "portainer"
|
"projectName": "portainer"
|
||||||
}
|
}
|
||||||
|
|
|
@ -149,8 +149,8 @@
|
||||||
<span ng-show="state.testInProgress">Test in progress...</span>
|
<span ng-show="state.testInProgress">Test in progress...</span>
|
||||||
</button>
|
</button>
|
||||||
<button type="button" class="btn btn-primary btn-sm" ng-disabled="state.updateInProgress || !state.validConfiguration" ng-click="updateConfiguration()" button-spinner="state.updateInProgress">
|
<button type="button" class="btn btn-primary btn-sm" ng-disabled="state.updateInProgress || !state.validConfiguration" ng-click="updateConfiguration()" button-spinner="state.updateInProgress">
|
||||||
<span ng-hide="state.updateInProgress">Update configuration</span>
|
<span ng-hide="state.updateInProgress">Save configuration</span>
|
||||||
<span ng-show="state.updateInProgress">Updating configuration...</span>
|
<span ng-show="state.updateInProgress">Saving configuration...</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -64,8 +64,8 @@
|
||||||
<a ui-sref="portainer.registries.registry.repositories({id: item.Id})" ng-if="$ctrl.registryManagement" class="space-left">
|
<a ui-sref="portainer.registries.registry.repositories({id: item.Id})" ng-if="$ctrl.registryManagement" class="space-left">
|
||||||
<i class="fa fa-search" aria-hidden="true"></i> Browse
|
<i class="fa fa-search" aria-hidden="true"></i> Browse
|
||||||
</a>
|
</a>
|
||||||
<a ui-sref="portainer.extensions.extension({id: 1})" ng-if="!$ctrl.registryManagement" class="space-left">
|
<a ui-sref="portainer.extensions.extension({id: 1})" ng-if="!$ctrl.registryManagement" class="space-left" style="color: #767676" tooltip-append-to-body="true" tooltip-placement="bottom" tooltip-class="portainer-tooltip" uib-tooltip="Feature available via an extension">
|
||||||
<i class="fa fa-search" aria-hidden="true"></i> Browse ( <extension-tooltip></extension-tooltip> )
|
<i class="fa fa-search" aria-hidden="true"></i> Browse (extension)
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
<!-- extension -->
|
<!-- extension -->
|
||||||
<div class="blocklist-item" ng-click="$ctrl.goToExtensionView()">
|
<div class="blocklist-item" ng-click="$ctrl.goToExtensionView()" ng-class="{ 'blocklist-item--disabled': !$ctrl.model.Available }">
|
||||||
<div class="blocklist-item-box">
|
<div class="blocklist-item-box">
|
||||||
<!-- extension-image -->
|
<!-- extension-image -->
|
||||||
<span ng-if="$ctrl.model.Logo">
|
<span ng-if="$ctrl.model.Logo" style="width: 75px; text-align: center;">
|
||||||
<img class="blocklist-item-logo" ng-src="{{ $ctrl.model.Logo }}" />
|
<!-- <img class="blocklist-item-logo" ng-src="{{ $ctrl.model.Logo }}" /> -->
|
||||||
|
<i class="{{ $ctrl.model.Logo }} fa fa-4x blue-icon" aria-hidden="true"></i>
|
||||||
</span>
|
</span>
|
||||||
<span class="blocklist-item-logo" ng-if="!$ctrl.model.Logo">
|
<span class="blocklist-item-logo" ng-if="!$ctrl.model.Logo">
|
||||||
<i class="fa fa-bolt fa-4x blue-icon" style="margin-left: 14px;" aria-hidden="true"></i>
|
<i class="fa fa-bolt fa-4x blue-icon" style="margin-left: 14px;" aria-hidden="true"></i>
|
||||||
|
|
|
@ -1,18 +1,20 @@
|
||||||
angular.module('portainer.app')
|
angular.module('portainer.app')
|
||||||
.controller('ExtensionItemController', ['$state',
|
.controller('ExtensionItemController', ['$state',
|
||||||
function ($state) {
|
function($state) {
|
||||||
|
|
||||||
var ctrl = this;
|
var ctrl = this;
|
||||||
ctrl.$onInit = $onInit;
|
ctrl.$onInit = $onInit;
|
||||||
ctrl.goToExtensionView = goToExtensionView;
|
ctrl.goToExtensionView = goToExtensionView;
|
||||||
|
|
||||||
function goToExtensionView() {
|
function goToExtensionView() {
|
||||||
$state.go('portainer.extensions.extension', { id: ctrl.model.Id });
|
if (ctrl.model.Available) {
|
||||||
}
|
$state.go('portainer.extensions.extension', { id: ctrl.model.Id });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function $onInit() {
|
function $onInit() {
|
||||||
if (ctrl.currentDate === ctrl.model.License.Expiration) {
|
if (ctrl.currentDate === ctrl.model.License.Expiration) {
|
||||||
ctrl.model.Expired = true;
|
ctrl.model.Expired = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}]);
|
}]);
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
<i class="fa fa-bolt orange-icon" aria-hidden="true" tooltip-append-to-body="true" tooltip-placement="bottom" tooltip-class="portainer-tooltip" uib-tooltip="Feature available via a plug-in"></i>
|
|
|
@ -1,3 +0,0 @@
|
||||||
angular.module('portainer.app').component('extensionTooltip', {
|
|
||||||
templateUrl: './extension-tooltip.html'
|
|
||||||
});
|
|
|
@ -71,7 +71,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<button type="submit" class="btn btn-primary btn-sm" ng-click="$ctrl.formAction()" ng-disabled="$ctrl.actionInProgress || !registryFormAzure.$valid" button-spinner="$ctrl.actionInProgress">
|
<button type="submit" class="btn btn-primary btn-sm" ng-disabled="$ctrl.actionInProgress || !registryFormAzure.$valid" button-spinner="$ctrl.actionInProgress">
|
||||||
<span ng-hide="$ctrl.actionInProgress">{{ $ctrl.formActionLabel }}</span>
|
<span ng-hide="$ctrl.actionInProgress">{{ $ctrl.formActionLabel }}</span>
|
||||||
<span ng-show="$ctrl.actionInProgress">In progress...</span>
|
<span ng-show="$ctrl.actionInProgress">In progress...</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -95,7 +95,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<button type="submit" class="btn btn-primary btn-sm" ng-click="$ctrl.formAction()" ng-disabled="$ctrl.actionInProgress || !registryFormCustom.$valid" button-spinner="$ctrl.actionInProgress">
|
<button type="submit" class="btn btn-primary btn-sm" ng-disabled="$ctrl.actionInProgress || !registryFormCustom.$valid" button-spinner="$ctrl.actionInProgress">
|
||||||
<span ng-hide="$ctrl.actionInProgress">{{ $ctrl.formActionLabel }}</span>
|
<span ng-hide="$ctrl.actionInProgress">{{ $ctrl.formActionLabel }}</span>
|
||||||
<span ng-show="$ctrl.actionInProgress">In progress...</span>
|
<span ng-show="$ctrl.actionInProgress">In progress...</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<button type="submit" class="btn btn-primary btn-sm" ng-click="$ctrl.formAction()" ng-disabled="$ctrl.actionInProgress || !registryFormQuay.$valid" button-spinner="$ctrl.actionInProgress">
|
<button type="submit" class="btn btn-primary btn-sm" ng-disabled="$ctrl.actionInProgress || !registryFormQuay.$valid" button-spinner="$ctrl.actionInProgress">
|
||||||
<span ng-hide="$ctrl.actionInProgress">{{ $ctrl.formActionLabel }}</span>
|
<span ng-hide="$ctrl.actionInProgress">{{ $ctrl.formActionLabel }}</span>
|
||||||
<span ng-show="$ctrl.actionInProgress">In progress...</span>
|
<span ng-show="$ctrl.actionInProgress">In progress...</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -11,7 +11,7 @@ export function ExtensionViewModel(data) {
|
||||||
this.License = data.License;
|
this.License = data.License;
|
||||||
this.Version = data.Version;
|
this.Version = data.Version;
|
||||||
this.UpdateAvailable = data.UpdateAvailable;
|
this.UpdateAvailable = data.UpdateAvailable;
|
||||||
this.ProductId = data.ProductId;
|
this.ShopURL = data.ShopURL;
|
||||||
this.Images = data.Images;
|
this.Images = data.Images;
|
||||||
this.Logo = data.Logo;
|
this.Logo = data.Logo;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,54 @@
|
||||||
</rd-header>
|
</rd-header>
|
||||||
|
|
||||||
<information-panel title-text="Information">
|
<information-panel title-text="Information">
|
||||||
<span class="small text-muted">
|
<span class="text-muted" style="font-size: 90%;">
|
||||||
<p>
|
<p>
|
||||||
Content to be defined
|
Portainer CE is a great way of managing clusters, provisioning containers and services and
|
||||||
|
managing container environment lifecycles. To extend the benefit of Portainer CE even
|
||||||
|
more, and to address the needs of larger, complex or critical environments, the Portainer
|
||||||
|
team provides a growing range of low-cost Extensions.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
As the diagram shows, running a successful production container environment requires a
|
||||||
|
range of capability across a number of complex technical areas.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p style="text-align: center; margin: 15px 0 15px 0;">
|
||||||
|
<img src="images/extensions_overview_diagram.png" alt="extensions overview">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Available through a simple subscription process from the menu, Portainer Extensions
|
||||||
|
answers this need and provides a simple way to enhance the functionality that Portainer
|
||||||
|
makes available through incremental capability in important areas.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The vision for Portainer is to be the standard management layer for container platforms. In
|
||||||
|
order to achieve this vision, Portainer CE will be extended across a range of new functional
|
||||||
|
areas. In order to ensure that Portainer remains the best choice for managing production
|
||||||
|
container platforms, the Portainer team have chosen a modular extensible design approach,
|
||||||
|
where additional capability can be added to the Portainer CE core as needed, and at very
|
||||||
|
low cost.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The advantage of an extensible design is clear: While a range of capability is available, only
|
||||||
|
necessary functionality is added as and when needed.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Our first extension is <a ui-sref="portainer.extensions.extension({id: 1})">Registry Manager</a>, available now. Others (such as
|
||||||
|
Single Sign On and Operations Management) are scheduled for the early part of 2019.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Portainer CE is the core of the Portainer management environments. Portainer CE will
|
||||||
|
continue to be developed and made freely available as part of our deep commitment to our
|
||||||
|
Open Source heritage and our user community. Portainer CE will always deliver great
|
||||||
|
functionality and remain the industry standard toolset for managing container-based
|
||||||
|
platforms.
|
||||||
</p>
|
</p>
|
||||||
</span>
|
</span>
|
||||||
</information-panel>
|
</information-panel>
|
||||||
|
@ -61,7 +106,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row" ng-if="extensions">
|
<div class="row" ng-if="extensions && extensions.length > 0">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<extension-list
|
<extension-list
|
||||||
current-date="state.currentDate"
|
current-date="state.currentDate"
|
||||||
|
@ -69,3 +114,12 @@
|
||||||
></extension-list>
|
></extension-list>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<information-panel title-text="Error" ng-if="extensions && extensions.length === 0">
|
||||||
|
<span class="small text-muted">
|
||||||
|
<p>
|
||||||
|
<i class="fa fa-exclamation-triangle orange-icon" aria-hidden="true"></i>
|
||||||
|
Portainer must be connected to the Internet to fetch the list of available extensions.
|
||||||
|
</p>
|
||||||
|
</span>
|
||||||
|
</information-panel>
|
||||||
|
|
|
@ -1,60 +1,61 @@
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
angular.module('portainer.app')
|
angular.module('portainer.app')
|
||||||
.controller('ExtensionsController', ['$scope', '$state', 'ExtensionService', 'Notifications',
|
.controller('ExtensionsController', ['$scope', '$state', 'ExtensionService', 'Notifications',
|
||||||
function ($scope, $state, ExtensionService, Notifications) {
|
function($scope, $state, ExtensionService, Notifications) {
|
||||||
|
|
||||||
$scope.state = {
|
$scope.state = {
|
||||||
actionInProgress: false,
|
actionInProgress: false,
|
||||||
currentDate: moment().format('YYYY-MM-dd')
|
currentDate: moment().format('YYYY-MM-dd')
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.formValues = {
|
$scope.formValues = {
|
||||||
License: ''
|
License: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
function initView() {
|
function initView() {
|
||||||
ExtensionService.extensions(true)
|
ExtensionService.extensions(true)
|
||||||
.then(function onSuccess(data) {
|
.then(function onSuccess(data) {
|
||||||
$scope.extensions = data;
|
$scope.extensions = data;
|
||||||
})
|
})
|
||||||
.catch(function onError(err) {
|
.catch(function onError(err) {
|
||||||
Notifications.error('Failure', err, 'Unable to access extension store');
|
$scope.extensions = [];
|
||||||
});
|
Notifications.error('Failure', err, 'Unable to access extension store');
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
$scope.enableExtension = function() {
|
$scope.enableExtension = function() {
|
||||||
var license = $scope.formValues.License;
|
var license = $scope.formValues.License;
|
||||||
|
|
||||||
$scope.state.actionInProgress = true;
|
$scope.state.actionInProgress = true;
|
||||||
ExtensionService.enable(license)
|
ExtensionService.enable(license)
|
||||||
.then(function onSuccess() {
|
.then(function onSuccess() {
|
||||||
Notifications.success('Extension successfully enabled');
|
Notifications.success('Extension successfully enabled');
|
||||||
$state.reload();
|
$state.reload();
|
||||||
})
|
})
|
||||||
.catch(function onError(err) {
|
.catch(function onError(err) {
|
||||||
Notifications.error('Failure', err, 'Unable to enable extension');
|
Notifications.error('Failure', err, 'Unable to enable extension');
|
||||||
})
|
})
|
||||||
.finally(function final() {
|
.finally(function final() {
|
||||||
$scope.state.actionInProgress = false;
|
$scope.state.actionInProgress = false;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
$scope.isValidLicenseFormat = function(form) {
|
$scope.isValidLicenseFormat = function(form) {
|
||||||
var valid = true;
|
var valid = true;
|
||||||
|
|
||||||
if (!$scope.formValues.License) {
|
if (!$scope.formValues.License) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isNaN($scope.formValues.License[0])) {
|
if (isNaN($scope.formValues.License[0])) {
|
||||||
valid = false;
|
valid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
form.extension_license.$setValidity('invalidLicense', valid);
|
form.extension_license.$setValidity('invalidLicense', valid);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
initView();
|
initView();
|
||||||
}]);
|
}]);
|
||||||
|
|
|
@ -51,11 +51,17 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="margin-top: 15px;" ng-if="!extension.Enabled && extension.Available">
|
<div style="margin-top: 15px;" ng-if="!extension.Enabled && extension.Available">
|
||||||
<a href="https://2-portainer.pi.bypronto.com/checkout/?add-to-cart={{ extension.ProductId }}&quantity={{ formValues.instances }}" target="_blank" class="btn btn-primary btn-sm" style="width: 100%; margin-left: 0;">
|
<a href="{{ extension.ShopURL }}&quantity={{ formValues.instances }}" target="_blank" class="btn btn-primary btn-sm" style="width: 100%; margin-left: 0;">
|
||||||
Buy
|
Buy
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-top: 10px;" ng-if="!extension.Enabled && extension.Available">
|
||||||
|
<a ui-sref="portainer.extensions" class="btn btn-primary btn-sm" style="width: 100%; margin-left: 0;">
|
||||||
|
Add license key
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div style="margin-top: 15px;" ng-if="!extension.Enabled && !extension.Available">
|
<div style="margin-top: 15px;" ng-if="!extension.Enabled && !extension.Available">
|
||||||
<btn class="btn btn-primary btn-sm" style="width: 100%; margin-left: 0;" disabled>Coming soon</btn>
|
<btn class="btn btn-primary btn-sm" style="width: 100%; margin-left: 0;" disabled>Coming soon</btn>
|
||||||
</div>
|
</div>
|
||||||
|
@ -92,10 +98,16 @@
|
||||||
Description
|
Description
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group" ng-if="extension.Description">
|
||||||
<span class="small text-muted">
|
<div class="text-muted" style="font-size: 90%;" ng-bind-html="extension.Description"></div>
|
||||||
{{ extension.Description }}
|
</div>
|
||||||
</span>
|
<div class="form-group" ng-if="!extension.Description">
|
||||||
|
<div class="small text-muted">
|
||||||
|
<p>
|
||||||
|
<i class="fa fa-exclamation-triangle orange-icon" aria-hidden="true"></i>
|
||||||
|
Description for this extension unavailable at the moment.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</rd-widget-body>
|
</rd-widget-body>
|
||||||
</rd-widget>
|
</rd-widget>
|
||||||
|
@ -113,7 +125,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div style="text-align: center;">
|
<div style="text-align: center;">
|
||||||
<div ng-repeat="image in extension.Images" style="margin-top: 25px; cursor: zoom-in;">
|
<div ng-repeat="image in extension.Images" style="margin-top: 25px; cursor: zoom-in;">
|
||||||
<img ng-src="{{image}}" ng-click="enlargeImage(image)"/>
|
<img ng-src="{{image}}" ng-click="enlargeImage(image)" style="max-width: 1024px;" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</rd-widget-body>
|
</rd-widget-body>
|
||||||
|
|
|
@ -52,7 +52,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="margin-top: 15px;" ng-disabled="!formValues.hostCount">
|
<div style="margin-top: 15px;" ng-disabled="!formValues.hostCount">
|
||||||
<a href="https://2-portainer.pi.bypronto.com/checkout/?add-to-cart={{ product.ProductId }}&quantity={{ formValues.hostCount }}" target="_blank" class="btn btn-primary btn-sm" style="width: 100%; margin-left: 0;">
|
<a href="https://portainer.io/checkout/?add-to-cart={{ product.ProductId }}&quantity={{ formValues.hostCount }}" target="_blank" class="btn btn-primary btn-sm" style="width: 100%; margin-left: 0;">
|
||||||
Buy
|
Buy
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -12,7 +12,7 @@ angular.module('portainer.app')
|
||||||
Id: 1,
|
Id: 1,
|
||||||
Name: 'Business Support Standard',
|
Name: 'Business Support Standard',
|
||||||
ShortDescription: '11x5 support with 4 hour response',
|
ShortDescription: '11x5 support with 4 hour response',
|
||||||
Price: 'USD 120',
|
Price: 'US$120.00',
|
||||||
PriceDescription: 'Price per month per host (minimum 10 hosts)',
|
PriceDescription: 'Price per month per host (minimum 10 hosts)',
|
||||||
Description: 'Portainer Business Support Standard:\n\n* 7am – 6pm business days, local time.\n* 4 Hour response for issues, 4 named support contacts.\n\nPortainer support provides you with an easy way to interact directly with the Portainer development team; whether you have an issue with the product, think you have found a bug, or need help on how to use Portainer, we are here to help. Support is initiated from our web based ticketing system, and support is provided either by Slack messaging, Zoom remote support, or email.\n\nPrice is per Docker Host, with a 10 Host minimum, and is an annual support subscription.',
|
Description: 'Portainer Business Support Standard:\n\n* 7am – 6pm business days, local time.\n* 4 Hour response for issues, 4 named support contacts.\n\nPortainer support provides you with an easy way to interact directly with the Portainer development team; whether you have an issue with the product, think you have found a bug, or need help on how to use Portainer, we are here to help. Support is initiated from our web based ticketing system, and support is provided either by Slack messaging, Zoom remote support, or email.\n\nPrice is per Docker Host, with a 10 Host minimum, and is an annual support subscription.',
|
||||||
ProductId: '1163'
|
ProductId: '1163'
|
||||||
|
@ -21,7 +21,7 @@ angular.module('portainer.app')
|
||||||
Id: 2,
|
Id: 2,
|
||||||
Name: 'Business Support Critical',
|
Name: 'Business Support Critical',
|
||||||
ShortDescription: '24x7 support with 1 hour response',
|
ShortDescription: '24x7 support with 1 hour response',
|
||||||
Price: 'USD 240',
|
Price: 'US$240.00',
|
||||||
PriceDescription: 'Price per month per host (minimum 10 hosts)',
|
PriceDescription: 'Price per month per host (minimum 10 hosts)',
|
||||||
Description: 'Portainer Business Support Critical:\n\n* 24x7\n* 1 Hour response for issues, 4 named support contacts.\n\nPortainer support provides you with advanced support for critical requirements. Business Support Critical is an easy way to interact directly with the Portainer development team; whether you have an issue with the product, think you have found a bug, or need help on how to use Portainer, we are here to help. Support is initiated from our web based ticketing system, and support is provided either by Slack messaging, Zoom remote support, or email.\n\nPrice is per Docker Host, with a 10 Host minimum, and is an annual support subscription.',
|
Description: 'Portainer Business Support Critical:\n\n* 24x7\n* 1 Hour response for issues, 4 named support contacts.\n\nPortainer support provides you with advanced support for critical requirements. Business Support Critical is an easy way to interact directly with the Portainer development team; whether you have an issue with the product, think you have found a bug, or need help on how to use Portainer, we are here to help. Support is initiated from our web based ticketing system, and support is provided either by Slack messaging, Zoom remote support, or email.\n\nPrice is per Docker Host, with a 10 Host minimum, and is an annual support subscription.',
|
||||||
ProductId: '1162'
|
ProductId: '1162'
|
||||||
|
|
|
@ -174,6 +174,11 @@ a[ng-click]{
|
||||||
box-shadow: 0 3px 10px -2px rgba(161, 170, 166, 0.5);
|
box-shadow: 0 3px 10px -2px rgba(161, 170, 166, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.blocklist-item--disabled {
|
||||||
|
cursor: auto;
|
||||||
|
background-color: #ececec;
|
||||||
|
}
|
||||||
|
|
||||||
.blocklist-item--selected {
|
.blocklist-item--selected {
|
||||||
border: 2px solid #bbbbbb;
|
border: 2px solid #bbbbbb;
|
||||||
background-color: #ececec;
|
background-color: #ececec;
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 79 KiB |
|
@ -1,5 +1,5 @@
|
||||||
Name: portainer
|
Name: portainer
|
||||||
Version: 1.20-dev
|
Version: 1.20.0
|
||||||
Release: 0
|
Release: 0
|
||||||
License: Zlib
|
License: Zlib
|
||||||
Summary: A lightweight docker management UI
|
Summary: A lightweight docker management UI
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"author": "Portainer.io",
|
"author": "Portainer.io",
|
||||||
"name": "portainer",
|
"name": "portainer",
|
||||||
"homepage": "http://portainer.io",
|
"homepage": "http://portainer.io",
|
||||||
"version": "1.20-dev",
|
"version": "1.20.0",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git@github.com:portainer/portainer.git"
|
"url": "git@github.com:portainer/portainer.git"
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
"description": "Docker image registry",
|
"description": "Docker image registry",
|
||||||
"categories": ["docker"],
|
"categories": ["docker"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/registry.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/registry.png",
|
||||||
"image": "registry:latest",
|
"image": "registry:latest",
|
||||||
"ports": [
|
"ports": [
|
||||||
"5000/tcp"
|
"5000/tcp"
|
||||||
|
@ -18,7 +18,7 @@
|
||||||
"description": "High performance web server",
|
"description": "High performance web server",
|
||||||
"categories": ["webserver"],
|
"categories": ["webserver"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/nginx.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/nginx.png",
|
||||||
"image": "nginx:latest",
|
"image": "nginx:latest",
|
||||||
"ports": [
|
"ports": [
|
||||||
"80/tcp",
|
"80/tcp",
|
||||||
|
@ -32,7 +32,7 @@
|
||||||
"description": "Open-source HTTP server",
|
"description": "Open-source HTTP server",
|
||||||
"categories": ["webserver"],
|
"categories": ["webserver"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/httpd.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/httpd.png",
|
||||||
"image": "httpd:latest",
|
"image": "httpd:latest",
|
||||||
"ports": [
|
"ports": [
|
||||||
"80/tcp"
|
"80/tcp"
|
||||||
|
@ -45,7 +45,7 @@
|
||||||
"description": "HTTP/2 web server with automatic HTTPS",
|
"description": "HTTP/2 web server with automatic HTTPS",
|
||||||
"categories": ["webserver"],
|
"categories": ["webserver"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/caddy.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/caddy.png",
|
||||||
"image": "abiosoft/caddy:latest",
|
"image": "abiosoft/caddy:latest",
|
||||||
"ports": [
|
"ports": [
|
||||||
"80/tcp", "443/tcp", "2015/tcp"
|
"80/tcp", "443/tcp", "2015/tcp"
|
||||||
|
@ -58,7 +58,7 @@
|
||||||
"description": "The most popular open-source database",
|
"description": "The most popular open-source database",
|
||||||
"categories": ["database"],
|
"categories": ["database"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/mysql.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/mysql.png",
|
||||||
"image": "mysql:latest",
|
"image": "mysql:latest",
|
||||||
"env": [
|
"env": [
|
||||||
{
|
{
|
||||||
|
@ -77,7 +77,7 @@
|
||||||
"description": "Performance beyond MySQL",
|
"description": "Performance beyond MySQL",
|
||||||
"categories": ["database"],
|
"categories": ["database"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/mariadb.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/mariadb.png",
|
||||||
"image": "mariadb:latest",
|
"image": "mariadb:latest",
|
||||||
"env": [
|
"env": [
|
||||||
{
|
{
|
||||||
|
@ -96,7 +96,7 @@
|
||||||
"description": "The most advanced open-source database",
|
"description": "The most advanced open-source database",
|
||||||
"categories": ["database"],
|
"categories": ["database"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/postgres.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/postgres.png",
|
||||||
"image": "postgres:latest",
|
"image": "postgres:latest",
|
||||||
"env": [
|
"env": [
|
||||||
{
|
{
|
||||||
|
@ -119,7 +119,7 @@
|
||||||
"description": "Open-source document-oriented database",
|
"description": "Open-source document-oriented database",
|
||||||
"categories": ["database"],
|
"categories": ["database"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/mongo.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/mongo.png",
|
||||||
"image": "mongo:latest",
|
"image": "mongo:latest",
|
||||||
"ports": [
|
"ports": [
|
||||||
"27017/tcp"
|
"27017/tcp"
|
||||||
|
@ -132,7 +132,7 @@
|
||||||
"description": "An open-source, survivable, strongly consistent, scale-out SQL database",
|
"description": "An open-source, survivable, strongly consistent, scale-out SQL database",
|
||||||
"categories": ["database"],
|
"categories": ["database"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/cockroachdb.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/cockroachdb.png",
|
||||||
"image": "cockroachdb/cockroach:latest",
|
"image": "cockroachdb/cockroach:latest",
|
||||||
"ports": [
|
"ports": [
|
||||||
"26257/tcp",
|
"26257/tcp",
|
||||||
|
@ -147,7 +147,7 @@
|
||||||
"description": "An open-source distributed SQL database",
|
"description": "An open-source distributed SQL database",
|
||||||
"categories": ["database"],
|
"categories": ["database"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/cratedb.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/cratedb.png",
|
||||||
"image": "crate:latest",
|
"image": "crate:latest",
|
||||||
"ports": [
|
"ports": [
|
||||||
"4200/tcp",
|
"4200/tcp",
|
||||||
|
@ -161,7 +161,7 @@
|
||||||
"description": "Open-source search and analytics engine",
|
"description": "Open-source search and analytics engine",
|
||||||
"categories": ["database"],
|
"categories": ["database"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/elasticsearch.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/elasticsearch.png",
|
||||||
"image": "elasticsearch:latest",
|
"image": "elasticsearch:latest",
|
||||||
"ports": [
|
"ports": [
|
||||||
"9200/tcp",
|
"9200/tcp",
|
||||||
|
@ -176,7 +176,7 @@
|
||||||
"note": "Default username is <b>root</b>. Check the <a href=\"https://docs.gitlab.com/omnibus/docker/README.html#after-starting-a-container\" target=\"_blank\">Gitlab documentation</a> to get started.",
|
"note": "Default username is <b>root</b>. Check the <a href=\"https://docs.gitlab.com/omnibus/docker/README.html#after-starting-a-container\" target=\"_blank\">Gitlab documentation</a> to get started.",
|
||||||
"categories": ["development", "project-management"],
|
"categories": ["development", "project-management"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/gitlab_ce.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/gitlab_ce.png",
|
||||||
"image": "gitlab/gitlab-ce:latest",
|
"image": "gitlab/gitlab-ce:latest",
|
||||||
"ports": [
|
"ports": [
|
||||||
"80/tcp",
|
"80/tcp",
|
||||||
|
@ -195,7 +195,7 @@
|
||||||
"description": "A distributed object storage server built for cloud applications and devops",
|
"description": "A distributed object storage server built for cloud applications and devops",
|
||||||
"categories": ["storage"],
|
"categories": ["storage"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/minio.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/minio.png",
|
||||||
"image": "minio/minio:latest",
|
"image": "minio/minio:latest",
|
||||||
"ports": [
|
"ports": [
|
||||||
"9000/tcp"
|
"9000/tcp"
|
||||||
|
@ -219,7 +219,7 @@
|
||||||
"description": "Standalone AWS S3 protocol server",
|
"description": "Standalone AWS S3 protocol server",
|
||||||
"categories": ["storage"],
|
"categories": ["storage"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/scality-s3.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/scality-s3.png",
|
||||||
"image": "scality/s3server",
|
"image": "scality/s3server",
|
||||||
"ports": [
|
"ports": [
|
||||||
"8000/tcp"
|
"8000/tcp"
|
||||||
|
@ -243,7 +243,7 @@
|
||||||
"categories": ["database"],
|
"categories": ["database"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"note": "Password needs to include at least 8 characters including uppercase, lowercase letters, base-10 digits and/or non-alphanumeric symbols.",
|
"note": "Password needs to include at least 8 characters including uppercase, lowercase letters, base-10 digits and/or non-alphanumeric symbols.",
|
||||||
"logo": "https://portainer.io/images/logos/microsoft.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/microsoft.png",
|
||||||
"image": "microsoft/mssql-server-linux:2017-GA",
|
"image": "microsoft/mssql-server-linux:2017-GA",
|
||||||
"ports": [
|
"ports": [
|
||||||
"1433/tcp"
|
"1433/tcp"
|
||||||
|
@ -266,7 +266,7 @@
|
||||||
"categories": ["database"],
|
"categories": ["database"],
|
||||||
"platform": "windows",
|
"platform": "windows",
|
||||||
"note": "Password needs to include at least 8 characters including uppercase, lowercase letters, base-10 digits and/or non-alphanumeric symbols.",
|
"note": "Password needs to include at least 8 characters including uppercase, lowercase letters, base-10 digits and/or non-alphanumeric symbols.",
|
||||||
"logo": "https://portainer.io/images/logos/microsoft.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/microsoft.png",
|
||||||
"image": "microsoft/mssql-server-windows-developer:latest",
|
"image": "microsoft/mssql-server-windows-developer:latest",
|
||||||
"ports": [
|
"ports": [
|
||||||
"1433/tcp"
|
"1433/tcp"
|
||||||
|
@ -290,7 +290,7 @@
|
||||||
"categories": ["database"],
|
"categories": ["database"],
|
||||||
"platform": "windows",
|
"platform": "windows",
|
||||||
"note": "Password needs to include at least 8 characters including uppercase, lowercase letters, base-10 digits and/or non-alphanumeric symbols.",
|
"note": "Password needs to include at least 8 characters including uppercase, lowercase letters, base-10 digits and/or non-alphanumeric symbols.",
|
||||||
"logo": "https://portainer.io/images/logos/microsoft.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/microsoft.png",
|
||||||
"image": "microsoft/mssql-server-windows-express:latest",
|
"image": "microsoft/mssql-server-windows-express:latest",
|
||||||
"ports": [
|
"ports": [
|
||||||
"1433/tcp"
|
"1433/tcp"
|
||||||
|
@ -313,7 +313,7 @@
|
||||||
"description": "Open-source serverless computing platform",
|
"description": "Open-source serverless computing platform",
|
||||||
"categories": ["serverless"],
|
"categories": ["serverless"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/ironfunctions.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/ironfunctions.png",
|
||||||
"image": "iron/functions:latest",
|
"image": "iron/functions:latest",
|
||||||
"ports": [
|
"ports": [
|
||||||
"8080/tcp"
|
"8080/tcp"
|
||||||
|
@ -327,7 +327,7 @@
|
||||||
"description": "Open-source user interface for IronFunctions",
|
"description": "Open-source user interface for IronFunctions",
|
||||||
"categories": ["serverless"],
|
"categories": ["serverless"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/ironfunctions.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/ironfunctions.png",
|
||||||
"image": "iron/functions-ui:latest",
|
"image": "iron/functions-ui:latest",
|
||||||
"ports": [
|
"ports": [
|
||||||
"4000/tcp"
|
"4000/tcp"
|
||||||
|
@ -347,7 +347,7 @@
|
||||||
"description": "Open-source enterprise search platform",
|
"description": "Open-source enterprise search platform",
|
||||||
"categories": ["search-engine"],
|
"categories": ["search-engine"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/solr.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/solr.png",
|
||||||
"image": "solr:latest",
|
"image": "solr:latest",
|
||||||
"ports": [
|
"ports": [
|
||||||
"8983/tcp"
|
"8983/tcp"
|
||||||
|
@ -360,7 +360,7 @@
|
||||||
"description": "Open-source in-memory data structure store",
|
"description": "Open-source in-memory data structure store",
|
||||||
"categories": ["database"],
|
"categories": ["database"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/redis.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/redis.png",
|
||||||
"image": "redis:latest",
|
"image": "redis:latest",
|
||||||
"ports": [
|
"ports": [
|
||||||
"6379/tcp"
|
"6379/tcp"
|
||||||
|
@ -373,7 +373,7 @@
|
||||||
"description": "Highly reliable enterprise messaging system",
|
"description": "Highly reliable enterprise messaging system",
|
||||||
"categories": ["messaging"],
|
"categories": ["messaging"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/rabbitmq.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/rabbitmq.png",
|
||||||
"image": "rabbitmq:latest",
|
"image": "rabbitmq:latest",
|
||||||
"ports": [
|
"ports": [
|
||||||
"5671/tcp",
|
"5671/tcp",
|
||||||
|
@ -388,7 +388,7 @@
|
||||||
"categories": ["blog"],
|
"categories": ["blog"],
|
||||||
"note": "Access the blog management interface under <code>/ghost/</code>.",
|
"note": "Access the blog management interface under <code>/ghost/</code>.",
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/ghost.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/ghost.png",
|
||||||
"image": "ghost:latest",
|
"image": "ghost:latest",
|
||||||
"ports": [
|
"ports": [
|
||||||
"2368/tcp"
|
"2368/tcp"
|
||||||
|
@ -402,7 +402,7 @@
|
||||||
"categories": ["CMS"],
|
"categories": ["CMS"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"note": "Default credentials: admin / changeme",
|
"note": "Default credentials: admin / changeme",
|
||||||
"logo": "https://portainer.io/images/logos/plesk.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/plesk.png",
|
||||||
"image": "plesk/plesk:preview",
|
"image": "plesk/plesk:preview",
|
||||||
"ports": [
|
"ports": [
|
||||||
"21/tcp", "80/tcp", "443/tcp", "8880/tcp", "8443/tcp", "8447/tcp"
|
"21/tcp", "80/tcp", "443/tcp", "8880/tcp", "8443/tcp", "8447/tcp"
|
||||||
|
@ -414,7 +414,7 @@
|
||||||
"description": "Another free and open-source CMS",
|
"description": "Another free and open-source CMS",
|
||||||
"categories": ["CMS"],
|
"categories": ["CMS"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/joomla.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/joomla.png",
|
||||||
"image": "joomla:latest",
|
"image": "joomla:latest",
|
||||||
"env": [
|
"env": [
|
||||||
{
|
{
|
||||||
|
@ -438,7 +438,7 @@
|
||||||
"description": "Open-source content management framework",
|
"description": "Open-source content management framework",
|
||||||
"categories": ["CMS"],
|
"categories": ["CMS"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/drupal.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/drupal.png",
|
||||||
"image": "drupal:latest",
|
"image": "drupal:latest",
|
||||||
"ports": [
|
"ports": [
|
||||||
"80/tcp"
|
"80/tcp"
|
||||||
|
@ -451,7 +451,7 @@
|
||||||
"description": "A free and open-source CMS built on top of Zope",
|
"description": "A free and open-source CMS built on top of Zope",
|
||||||
"categories": ["CMS"],
|
"categories": ["CMS"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/plone.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/plone.png",
|
||||||
"image": "plone:latest",
|
"image": "plone:latest",
|
||||||
"ports": [
|
"ports": [
|
||||||
"8080/tcp"
|
"8080/tcp"
|
||||||
|
@ -464,7 +464,7 @@
|
||||||
"description": "Open-source e-commerce platform",
|
"description": "Open-source e-commerce platform",
|
||||||
"categories": ["CMS"],
|
"categories": ["CMS"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/magento.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/magento.png",
|
||||||
"image": "alankent/gsd:latest",
|
"image": "alankent/gsd:latest",
|
||||||
"ports": [
|
"ports": [
|
||||||
"80/tcp",
|
"80/tcp",
|
||||||
|
@ -479,7 +479,7 @@
|
||||||
"description": "Collect logs, metrics and docker events",
|
"description": "Collect logs, metrics and docker events",
|
||||||
"categories": ["Log Management", "Monitoring"],
|
"categories": ["Log Management", "Monitoring"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/sematext_agent.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/sematext_agent.png",
|
||||||
"image": "sematext/sematext-agent-docker:latest",
|
"image": "sematext/sematext-agent-docker:latest",
|
||||||
"name": "sematext-agent",
|
"name": "sematext-agent",
|
||||||
"privileged": true,
|
"privileged": true,
|
||||||
|
@ -506,7 +506,7 @@
|
||||||
"description": "Collect events and metrics",
|
"description": "Collect events and metrics",
|
||||||
"categories": ["Monitoring"],
|
"categories": ["Monitoring"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/datadog_agent.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/datadog_agent.png",
|
||||||
"image": "datadog/agent:latest",
|
"image": "datadog/agent:latest",
|
||||||
"env": [
|
"env": [
|
||||||
{
|
{
|
||||||
|
@ -538,7 +538,7 @@
|
||||||
"description": "Open-source marketing automation platform",
|
"description": "Open-source marketing automation platform",
|
||||||
"categories": ["marketing"],
|
"categories": ["marketing"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/mautic.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/mautic.png",
|
||||||
"image": "mautic/mautic:latest",
|
"image": "mautic/mautic:latest",
|
||||||
"env": [
|
"env": [
|
||||||
{
|
{
|
||||||
|
@ -562,7 +562,7 @@
|
||||||
"description": "Streaming media server",
|
"description": "Streaming media server",
|
||||||
"categories": ["streaming"],
|
"categories": ["streaming"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/wowza.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/wowza.png",
|
||||||
"image": "sameersbn/wowza:4.1.2-8",
|
"image": "sameersbn/wowza:4.1.2-8",
|
||||||
"env": [
|
"env": [
|
||||||
{
|
{
|
||||||
|
@ -589,7 +589,7 @@
|
||||||
"description": "Open-source continuous integration tool",
|
"description": "Open-source continuous integration tool",
|
||||||
"categories": ["continuous-integration"],
|
"categories": ["continuous-integration"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/jenkins.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/jenkins.png",
|
||||||
"image": "jenkins/jenkins:lts",
|
"image": "jenkins/jenkins:lts",
|
||||||
"ports": [
|
"ports": [
|
||||||
"8080/tcp",
|
"8080/tcp",
|
||||||
|
@ -609,7 +609,7 @@
|
||||||
"description": "Open-source project management tool",
|
"description": "Open-source project management tool",
|
||||||
"categories": ["project-management"],
|
"categories": ["project-management"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/redmine.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/redmine.png",
|
||||||
"image": "redmine:latest",
|
"image": "redmine:latest",
|
||||||
"ports": [
|
"ports": [
|
||||||
"3000/tcp"
|
"3000/tcp"
|
||||||
|
@ -622,7 +622,7 @@
|
||||||
"description": "Open-source business apps",
|
"description": "Open-source business apps",
|
||||||
"categories": ["project-management"],
|
"categories": ["project-management"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/odoo.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/odoo.png",
|
||||||
"image": "odoo:latest",
|
"image": "odoo:latest",
|
||||||
"env": [
|
"env": [
|
||||||
{
|
{
|
||||||
|
@ -651,7 +651,7 @@
|
||||||
"categories": ["backup"],
|
"categories": ["backup"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"note": "This application web interface is exposed on the port 55414 inside the container.",
|
"note": "This application web interface is exposed on the port 55414 inside the container.",
|
||||||
"logo": "https://portainer.io/images/logos/urbackup.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/urbackup.png",
|
||||||
"image": "cfstras/urbackup",
|
"image": "cfstras/urbackup",
|
||||||
"ports": [
|
"ports": [
|
||||||
"55413/tcp", "55414/tcp", "55415/tcp", "35622/tcp"
|
"55413/tcp", "55414/tcp", "55415/tcp", "35622/tcp"
|
||||||
|
@ -665,7 +665,7 @@
|
||||||
"note": "Default credentials: admin/admin",
|
"note": "Default credentials: admin/admin",
|
||||||
"categories": ["filesystem", "storage"],
|
"categories": ["filesystem", "storage"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/filebrowser.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/filebrowser.png",
|
||||||
"image": "filebrowser/filebrowser:latest",
|
"image": "filebrowser/filebrowser:latest",
|
||||||
"ports": [
|
"ports": [
|
||||||
"80/tcp"
|
"80/tcp"
|
||||||
|
@ -679,7 +679,7 @@
|
||||||
"description": "ColdFusion (CFML) CLI",
|
"description": "ColdFusion (CFML) CLI",
|
||||||
"categories": ["development"],
|
"categories": ["development"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/ortussolutions-commandbox.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/ortussolutions-commandbox.png",
|
||||||
"image": "ortussolutions/commandbox:latest",
|
"image": "ortussolutions/commandbox:latest",
|
||||||
"env": [
|
"env": [
|
||||||
{
|
{
|
||||||
|
@ -697,7 +697,7 @@
|
||||||
"description": "Open-source modular CMS",
|
"description": "Open-source modular CMS",
|
||||||
"categories": ["CMS"],
|
"categories": ["CMS"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/ortussolutions-contentbox.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/ortussolutions-contentbox.png",
|
||||||
"image": "ortussolutions/contentbox:latest",
|
"image": "ortussolutions/contentbox:latest",
|
||||||
"env": [
|
"env": [
|
||||||
{
|
{
|
||||||
|
@ -725,7 +725,7 @@
|
||||||
"note": "The agent will be deployed globally inside your cluster and available on port 9001.",
|
"note": "The agent will be deployed globally inside your cluster and available on port 9001.",
|
||||||
"categories": ["portainer"],
|
"categories": ["portainer"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/portainer.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/portainer.png",
|
||||||
"repository": {
|
"repository": {
|
||||||
"url": "https://github.com/portainer/templates",
|
"url": "https://github.com/portainer/templates",
|
||||||
"stackfile": "stacks/portainer-agent/docker-stack.yml"
|
"stackfile": "stacks/portainer-agent/docker-stack.yml"
|
||||||
|
@ -739,7 +739,7 @@
|
||||||
"note": "Deploys the API gateway and sample functions. You can access the UI on port 8080. <b>Warning</b>: the name of the stack must be 'func'.",
|
"note": "Deploys the API gateway and sample functions. You can access the UI on port 8080. <b>Warning</b>: the name of the stack must be 'func'.",
|
||||||
"categories": ["serverless"],
|
"categories": ["serverless"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/openfaas.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/openfaas.png",
|
||||||
"repository": {
|
"repository": {
|
||||||
"url": "https://github.com/openfaas/faas",
|
"url": "https://github.com/openfaas/faas",
|
||||||
"stackfile": "docker-compose.yml"
|
"stackfile": "docker-compose.yml"
|
||||||
|
@ -752,7 +752,7 @@
|
||||||
"note": "Deploys the IronFunctions API and UI.",
|
"note": "Deploys the IronFunctions API and UI.",
|
||||||
"categories": ["serverless"],
|
"categories": ["serverless"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/ironfunctions.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/ironfunctions.png",
|
||||||
"repository": {
|
"repository": {
|
||||||
"url": "https://github.com/portainer/templates",
|
"url": "https://github.com/portainer/templates",
|
||||||
"stackfile": "stacks/ironfunctions/docker-stack.yml"
|
"stackfile": "stacks/ironfunctions/docker-stack.yml"
|
||||||
|
@ -765,7 +765,7 @@
|
||||||
"note": "Deploys an insecure CockroachDB cluster, please refer to <a href=\"https://www.cockroachlabs.com/docs/stable/orchestrate-cockroachdb-with-docker-swarm.html\" target=\"_blank\">CockroachDB documentation</a> for production deployments.",
|
"note": "Deploys an insecure CockroachDB cluster, please refer to <a href=\"https://www.cockroachlabs.com/docs/stable/orchestrate-cockroachdb-with-docker-swarm.html\" target=\"_blank\">CockroachDB documentation</a> for production deployments.",
|
||||||
"categories": ["database"],
|
"categories": ["database"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/cockroachdb.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/cockroachdb.png",
|
||||||
"repository": {
|
"repository": {
|
||||||
"url": "https://github.com/portainer/templates",
|
"url": "https://github.com/portainer/templates",
|
||||||
"stackfile": "stacks/cockroachdb/docker-stack.yml"
|
"stackfile": "stacks/cockroachdb/docker-stack.yml"
|
||||||
|
@ -778,7 +778,7 @@
|
||||||
"note": "Deploys a Wordpress instance connected to a MySQL database.",
|
"note": "Deploys a Wordpress instance connected to a MySQL database.",
|
||||||
"categories": ["CMS"],
|
"categories": ["CMS"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/wordpress.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/wordpress.png",
|
||||||
"repository": {
|
"repository": {
|
||||||
"url": "https://github.com/portainer/templates",
|
"url": "https://github.com/portainer/templates",
|
||||||
"stackfile": "stacks/wordpress/docker-stack.yml"
|
"stackfile": "stacks/wordpress/docker-stack.yml"
|
||||||
|
@ -798,7 +798,7 @@
|
||||||
"note": "Deploys a Wordpress instance connected to a MySQL database.",
|
"note": "Deploys a Wordpress instance connected to a MySQL database.",
|
||||||
"categories": ["CMS"],
|
"categories": ["CMS"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/wordpress.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/wordpress.png",
|
||||||
"repository": {
|
"repository": {
|
||||||
"url": "https://github.com/portainer/templates",
|
"url": "https://github.com/portainer/templates",
|
||||||
"stackfile": "stacks/wordpress/docker-compose.yml"
|
"stackfile": "stacks/wordpress/docker-compose.yml"
|
||||||
|
@ -817,7 +817,7 @@
|
||||||
"description": "Microsoft Operations Management Suite Linux agent.",
|
"description": "Microsoft Operations Management Suite Linux agent.",
|
||||||
"categories": ["OPS"],
|
"categories": ["OPS"],
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"logo": "https://portainer.io/images/logos/microsoft.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/microsoft.png",
|
||||||
"repository": {
|
"repository": {
|
||||||
"url": "https://github.com/portainer/templates",
|
"url": "https://github.com/portainer/templates",
|
||||||
"stackfile": "stacks/microsoft-oms/docker-stack.yml"
|
"stackfile": "stacks/microsoft-oms/docker-stack.yml"
|
||||||
|
@ -840,7 +840,7 @@
|
||||||
"type": 2,
|
"type": 2,
|
||||||
"categories": ["Log Management", "Monitoring"],
|
"categories": ["Log Management", "Monitoring"],
|
||||||
"description": "Collect logs, metrics and docker events",
|
"description": "Collect logs, metrics and docker events",
|
||||||
"logo": "https://portainer.io/images/logos/sematext_agent.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/sematext_agent.png",
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"repository": {
|
"repository": {
|
||||||
"url": "https://github.com/portainer/templates",
|
"url": "https://github.com/portainer/templates",
|
||||||
|
@ -862,7 +862,7 @@
|
||||||
"type": 2,
|
"type": 2,
|
||||||
"categories": ["Monitoring"],
|
"categories": ["Monitoring"],
|
||||||
"description": "Collect events and metrics",
|
"description": "Collect events and metrics",
|
||||||
"logo": "https://portainer.io/images/logos/datadog_agent.png",
|
"logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/datadog_agent.png",
|
||||||
"platform": "linux",
|
"platform": "linux",
|
||||||
"repository": {
|
"repository": {
|
||||||
"url": "https://github.com/portainer/templates",
|
"url": "https://github.com/portainer/templates",
|
||||||
|
|
Loading…
Reference in New Issue