Skip to content

Commit 5d84e1e

Browse files
allmightyspiffGitHub Enterprise
authored andcommitted
Merge pull request #666 from Edson-Rios/issue663
Added new command - ibmcloud sl cdn create
2 parents 3a45d30 + dfd45f1 commit 5d84e1e

7 files changed

Lines changed: 383 additions & 0 deletions

File tree

plugin/commands/cdn/cdn.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ func SetupCobraCommands(sl *metadata.SoftlayerCommand) *cobra.Command {
1717
cobraCmd.AddCommand(NewListCommand(sl).Command)
1818
cobraCmd.AddCommand(NewDetailCommand(sl).Command)
1919
cobraCmd.AddCommand(NewEditCommand(sl).Command)
20+
cobraCmd.AddCommand(NewCreateCommand(sl).Command)
2021
return cobraCmd
2122
}
2223

plugin/commands/cdn/cdn_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ var availableCommands = []string{
2121
"detail",
2222
"edit",
2323
"list",
24+
"create",
2425
}
2526

2627
// This test suite exists to make sure commands don't get accidently removed from the actionBindings

plugin/commands/cdn/create.go

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package cdn
2+
3+
import (
4+
"github.com/IBM-Cloud/ibm-cloud-cli-sdk/bluemix/terminal"
5+
"github.com/softlayer/softlayer-go/datatypes"
6+
"github.com/spf13/cobra"
7+
8+
"github.ibm.com/SoftLayer/softlayer-cli/plugin/errors"
9+
. "github.ibm.com/SoftLayer/softlayer-cli/plugin/i18n"
10+
"github.ibm.com/SoftLayer/softlayer-cli/plugin/managers"
11+
"github.ibm.com/SoftLayer/softlayer-cli/plugin/metadata"
12+
"github.ibm.com/SoftLayer/softlayer-cli/plugin/utils"
13+
)
14+
15+
type CreateCommand struct {
16+
*metadata.SoftlayerCommand
17+
CdnManager managers.CdnManager
18+
Command *cobra.Command
19+
HostName string
20+
OriginHost string
21+
OriginType string
22+
Http int
23+
Https int
24+
BucketName string
25+
CName string
26+
Header string
27+
Path string
28+
Ssl string
29+
}
30+
31+
func NewCreateCommand(sl *metadata.SoftlayerCommand) *CreateCommand {
32+
thisCmd := &CreateCommand{
33+
SoftlayerCommand: sl,
34+
CdnManager: managers.NewCdnManager(sl.Session),
35+
}
36+
cobraCmd := &cobra.Command{
37+
Use: "create",
38+
Short: T("Create a new CDN domain mapping"),
39+
Long: T(`${COMMAND_NAME} sl cdn create
40+
Example:
41+
${COMMAND_NAME} sl cdn create --hostname www.example.com --origin 123.45.67.8 --http 80`),
42+
Args: metadata.NoArgs,
43+
RunE: func(cmd *cobra.Command, args []string) error {
44+
return thisCmd.Run(args)
45+
},
46+
}
47+
cobraCmd.Flags().StringVar(&thisCmd.HostName, "hostname", "", T("To route requests to your website, enter the hostname for your website, for example, www.example.com or app.example.com. [required]"))
48+
cobraCmd.Flags().StringVar(&thisCmd.OriginHost, "origin", "", T("Your server IP address or hostname. [required]"))
49+
cobraCmd.Flags().StringVar(&thisCmd.OriginType, "origin-type", "server", T("The origin type. [Permit: server, storage] Note: If OriginType is storage then OriginHost is take as Endpoint"))
50+
cobraCmd.Flags().IntVar(&thisCmd.Http, "http", 0, T("Http port"))
51+
cobraCmd.Flags().IntVar(&thisCmd.Https, "https", 0, T("Https port"))
52+
cobraCmd.Flags().StringVar(&thisCmd.BucketName, "bucket-name", "", T("Bucket name"))
53+
cobraCmd.Flags().StringVar(&thisCmd.CName, "cname", "", T("Enter a globally unique subdomain. The full URL becomes the CNAME we use to configure your DNS. If no value is entered, we will generate a CNAME for you."))
54+
cobraCmd.Flags().StringVar(&thisCmd.Header, "header", "", T("The edge server uses the host header in the HTTP header to communicate with the Origin host. It defaults to Hostname."))
55+
cobraCmd.Flags().StringVar(&thisCmd.Path, "path", "", T("Give a path relative to the domain provided, which can be used to reach this Origin. For example, 'articles/video' => 'www.example.com/articles/video"))
56+
cobraCmd.Flags().StringVar(&thisCmd.Ssl, "ssl", "dvSan", T("A DV SAN Certificate allows HTTPS traffic over your personal domain, but it requires a domain validation to prove ownership. A wildcard certificate allows HTTPS traffic only when using the CNAME given. [Permit: dvSan, wilcard]"))
57+
58+
//#nosec G104 -- This is a false positive
59+
cobraCmd.MarkFlagRequired("hostname")
60+
//#nosec G104 -- This is a false positive
61+
cobraCmd.MarkFlagRequired("origin")
62+
thisCmd.Command = cobraCmd
63+
return thisCmd
64+
}
65+
66+
func (cmd *CreateCommand) Run(args []string) error {
67+
if cmd.Http == 0 && cmd.Https == 0 {
68+
return errors.NewMissingInputError("http or https")
69+
}
70+
if cmd.OriginType != "server" && cmd.OriginType != "storage" {
71+
return errors.NewInvalidUsageError("--origintype")
72+
}
73+
if cmd.Ssl != "dvSan" && cmd.Ssl != "wilcard" && cmd.Ssl != "" {
74+
return errors.NewInvalidUsageError("--ssl")
75+
}
76+
77+
outputFormat := cmd.GetOutputFlag()
78+
79+
newCdn, err := cmd.CdnManager.CreateCdn(cmd.HostName, cmd.OriginHost, cmd.OriginType, cmd.Http, cmd.Https, cmd.BucketName, cmd.CName, cmd.Header, cmd.Path, cmd.Ssl)
80+
if err != nil {
81+
return errors.NewAPIError(T("Failed to create a CDN."), err.Error(), 2)
82+
}
83+
84+
PrintCndCreated(cmd.UI, newCdn, outputFormat)
85+
return nil
86+
}
87+
88+
func PrintCndCreated(ui terminal.UI, cdn []datatypes.Container_Network_CdnMarketplace_Configuration_Mapping, outputFormat string) {
89+
table := ui.Table([]string{
90+
T("Name"),
91+
T("Value"),
92+
})
93+
if len(cdn) > 0 {
94+
table.Add(T("CDN Unique ID"), utils.FormatStringPointer(cdn[0].UniqueId))
95+
if cdn[0].BucketName != nil {
96+
table.Add(T("Bucket Name"), utils.FormatStringPointer(cdn[0].BucketName))
97+
}
98+
table.Add(T("Hostname"), utils.FormatStringPointer(cdn[0].Domain))
99+
table.Add(T("Header"), utils.FormatStringPointer(cdn[0].Header))
100+
table.Add(T("IBM CNAME"), utils.FormatStringPointer(cdn[0].Cname))
101+
table.Add(T("Akamai CNAME"), utils.FormatStringPointer(cdn[0].AkamaiCname))
102+
table.Add(T("Origin Host"), utils.FormatStringPointer(cdn[0].OriginHost))
103+
table.Add(T("Origin Type"), utils.FormatStringPointer(cdn[0].OriginType))
104+
table.Add(T("Protocol"), utils.FormatStringPointer(cdn[0].Protocol))
105+
table.Add(T("Http Port"), utils.FormatIntPointer(cdn[0].HttpPort))
106+
table.Add(T("Https Port"), utils.FormatIntPointer(cdn[0].HttpsPort))
107+
table.Add(T("Certificate Type"), utils.FormatStringPointer(cdn[0].CertificateType))
108+
table.Add(T("Provider"), utils.FormatStringPointer(cdn[0].VendorName))
109+
table.Add(T("Path"), utils.FormatStringPointer(cdn[0].Path))
110+
}
111+
utils.PrintTable(ui, table, outputFormat)
112+
}

plugin/commands/cdn/create_test.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package cdn_test
2+
3+
import (
4+
"github.com/IBM-Cloud/ibm-cloud-cli-sdk/testhelpers/terminal"
5+
. "github.com/onsi/ginkgo"
6+
. "github.com/onsi/gomega"
7+
"github.com/softlayer/softlayer-go/session"
8+
9+
"github.ibm.com/SoftLayer/softlayer-cli/plugin/commands/cdn"
10+
"github.ibm.com/SoftLayer/softlayer-cli/plugin/metadata"
11+
"github.ibm.com/SoftLayer/softlayer-cli/plugin/testhelpers"
12+
)
13+
14+
var _ = Describe("Cdn create", func() {
15+
var (
16+
fakeUI *terminal.FakeUI
17+
cliCommand *cdn.CreateCommand
18+
fakeSession *session.Session
19+
slCommand *metadata.SoftlayerCommand
20+
)
21+
BeforeEach(func() {
22+
fakeUI = terminal.NewFakeUI()
23+
fakeSession = testhelpers.NewFakeSoftlayerSession([]string{})
24+
slCommand = metadata.NewSoftlayerCommand(fakeUI, fakeSession)
25+
cliCommand = cdn.NewCreateCommand(slCommand)
26+
cliCommand.Command.PersistentFlags().Var(cliCommand.OutputFlag, "output", "--output=JSON for json output.")
27+
})
28+
29+
Describe("Cdn create", func() {
30+
Context("Cdn create, Invalid Usage", func() {
31+
It("Set command with an invalid output option", func() {
32+
err := testhelpers.RunCobraCommand(cliCommand.Command, "--output=xml")
33+
Expect(err).To(HaveOccurred())
34+
Expect(err.Error()).To(ContainSubstring("Incorrect Usage: Invalid output format, only JSON is supported now."))
35+
})
36+
It("Set command without flag hostname", func() {
37+
err := testhelpers.RunCobraCommand(cliCommand.Command)
38+
Expect(err).To(HaveOccurred())
39+
Expect(err.Error()).To(ContainSubstring(`required flag(s) "hostname", "origin" not set`))
40+
})
41+
It("Set command without flag origin", func() {
42+
err := testhelpers.RunCobraCommand(cliCommand.Command, "--hostname", "www.example.com")
43+
Expect(err).To(HaveOccurred())
44+
Expect(err.Error()).To(ContainSubstring(`required flag(s) "origin" not set`))
45+
})
46+
It("Set command without flag http or https", func() {
47+
err := testhelpers.RunCobraCommand(cliCommand.Command, "--hostname", "www.example.com", "--origin", "123.45.67.8")
48+
Expect(err).To(HaveOccurred())
49+
Expect(err.Error()).To(ContainSubstring(`Incorrect Usage: 'http or https' is required`))
50+
})
51+
It("Set command with flag wrong origin-type", func() {
52+
err := testhelpers.RunCobraCommand(cliCommand.Command, "--hostname", "www.example.com", "--origin", "123.45.67.8", "--http", "80", "--origin-type", "asdfgh")
53+
Expect(err).To(HaveOccurred())
54+
Expect(err.Error()).To(ContainSubstring(`Incorrect Usage: --origintype`))
55+
})
56+
It("Set command with flag wrong ssl", func() {
57+
err := testhelpers.RunCobraCommand(cliCommand.Command, "--hostname", "www.example.com", "--origin", "123.45.67.8", "--http", "80", "--ssl", "asdfgh")
58+
Expect(err).To(HaveOccurred())
59+
Expect(err.Error()).To(ContainSubstring(`Incorrect Usage: --ssl`))
60+
})
61+
})
62+
63+
Context("Cdn create, correct use", func() {
64+
It("return cdn created", func() {
65+
err := testhelpers.RunCobraCommand(cliCommand.Command, "--hostname", "www.example.com", "--origin", "123.45.67.8", "--http", "80")
66+
Expect(err).NotTo(HaveOccurred())
67+
Expect(fakeUI.Outputs()).To(ContainSubstring("CDN Unique ID"))
68+
Expect(fakeUI.Outputs()).To(ContainSubstring("354034879028850"))
69+
Expect(fakeUI.Outputs()).To(ContainSubstring("Bucket Name"))
70+
Expect(fakeUI.Outputs()).To(ContainSubstring("test-bucket-name"))
71+
Expect(fakeUI.Outputs()).To(ContainSubstring("Hostname"))
72+
Expect(fakeUI.Outputs()).To(ContainSubstring("test.com"))
73+
Expect(fakeUI.Outputs()).To(ContainSubstring("Header"))
74+
Expect(fakeUI.Outputs()).To(ContainSubstring("header.test.com"))
75+
Expect(fakeUI.Outputs()).To(ContainSubstring("IBM CNAME"))
76+
Expect(fakeUI.Outputs()).To(ContainSubstring("test.cdn.appdomain.cloud"))
77+
Expect(fakeUI.Outputs()).To(ContainSubstring("Protocol"))
78+
Expect(fakeUI.Outputs()).To(ContainSubstring("HOST_SERVER"))
79+
Expect(fakeUI.Outputs()).To(ContainSubstring("Certificate Type"))
80+
Expect(fakeUI.Outputs()).To(ContainSubstring("WILDCARD_CERT"))
81+
})
82+
It("return cdn in format json", func() {
83+
err := testhelpers.RunCobraCommand(cliCommand.Command, "--hostname", "www.example.com", "--origin", "123.45.67.8", "--http", "80", "--output", "json")
84+
Expect(err).NotTo(HaveOccurred())
85+
Expect(fakeUI.Outputs()).To(ContainSubstring(`"Name": "CDN Unique ID",`))
86+
Expect(fakeUI.Outputs()).To(ContainSubstring(`"Value": "354034879028850"`))
87+
Expect(fakeUI.Outputs()).To(ContainSubstring(`[`))
88+
Expect(fakeUI.Outputs()).To(ContainSubstring(`{`))
89+
Expect(fakeUI.Outputs()).To(ContainSubstring(`}`))
90+
Expect(fakeUI.Outputs()).To(ContainSubstring(`]`))
91+
})
92+
})
93+
})
94+
})

plugin/managers/cdn.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@ import (
88
"github.com/softlayer/softlayer-go/datatypes"
99
"github.com/softlayer/softlayer-go/services"
1010
"github.com/softlayer/softlayer-go/session"
11+
"github.com/softlayer/softlayer-go/sl"
1112
)
1213

1314
type CdnManager interface {
1415
GetNetworkCdnMarketplaceConfigurationMapping() ([]datatypes.Container_Network_CdnMarketplace_Configuration_Mapping, error)
1516
GetDetailCDN(uniqueId int, mask string) (datatypes.Container_Network_CdnMarketplace_Configuration_Mapping, error)
1617
GetUsageMetrics(uniqueId int, history int, mask string) (datatypes.Container_Network_CdnMarketplace_Metrics, error)
1718
EditCDN(uniqueId int, header string, httpPort int, httpsPort int, origin string, respectHeaders string, cache string, cacheDescription string, performanceConfiguration string) (datatypes.Container_Network_CdnMarketplace_Configuration_Mapping, error)
19+
CreateCdn(hostname string, originHost string, originType string, http int, https int, bucketName string, cname string, header string, path string, ssl string) ([]datatypes.Container_Network_CdnMarketplace_Configuration_Mapping, error)
1820
}
1921

2022
type cdnManager struct {
@@ -137,3 +139,54 @@ func (a cdnManager) EditCDN(uniqueId int, header string, httpPort int, httpsPort
137139
}
138140
return cdn[0], nil
139141
}
142+
143+
/*
144+
https://sldn.softlayer.com/reference/services/SoftLayer_Network_CdnMarketplace_Configuration_Mapping/createDomainMapping/
145+
*/
146+
func (a cdnManager) CreateCdn(hostname string, originHost string, originType string, http int, https int, bucketName string, cname string, header string, path string, ssl string) ([]datatypes.Container_Network_CdnMarketplace_Configuration_Mapping, error) {
147+
types := map[string]string{
148+
"server": "HOST_SERVER",
149+
"storage": "OBJECT_STORAGE",
150+
}
151+
sslCertificate := map[string]string{
152+
"wilcard": "WILDCARD_CERT",
153+
"dvSan": "SHARED_SAN_CERT",
154+
}
155+
156+
NewOrigin := datatypes.Container_Network_CdnMarketplace_Configuration_Input{
157+
Domain: sl.String(hostname),
158+
Origin: sl.String(originHost),
159+
OriginType: sl.String(types[originType]),
160+
VendorName: sl.String("akamai"),
161+
}
162+
163+
protocol := ""
164+
if http != 0 {
165+
protocol = "HTTP"
166+
NewOrigin.HttpPort = sl.Int(http)
167+
}
168+
if https != 0 {
169+
protocol = "HTTPS"
170+
NewOrigin.HttpsPort = sl.Int(https)
171+
NewOrigin.CertificateType = sl.String(sslCertificate[ssl])
172+
}
173+
if http != 0 && https != 0 {
174+
protocol = "HTTP_AND_HTTPS"
175+
}
176+
NewOrigin.Protocol = sl.String(protocol)
177+
if types[originType] == "OBJECT_STORAGE" {
178+
NewOrigin.BucketName = sl.String(bucketName)
179+
NewOrigin.Header = sl.String(originHost)
180+
}
181+
if cname != "" {
182+
NewOrigin.Cname = sl.String(cname + ".cdn.appdomain.cloud")
183+
}
184+
if header != "" {
185+
NewOrigin.Header = sl.String(header)
186+
}
187+
if path != "" {
188+
NewOrigin.Path = sl.String("/" + path)
189+
}
190+
191+
return a.CdnService.CreateDomainMapping(&NewOrigin)
192+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
[
2+
{
3+
"bucketName": "test-bucket-name",
4+
"akamaiCname": "wildcard.appdomain.mdc.edgekey.net",
5+
"cacheKeyQueryRule": "include-all",
6+
"certificateType": "WILDCARD_CERT",
7+
"cname": "test.cdn.appdomain.cloud",
8+
"createDate": "2020-09-29T15:19:01-06:00",
9+
"domain": "test.com",
10+
"header": "header.test.com",
11+
"httpPort": 83,
12+
"httpsPort": null,
13+
"modifyDate": "2021-06-24T09:02:22-06:00",
14+
"originHost": "10.32.12.125",
15+
"originType": "HOST_SERVER",
16+
"path": "\/*",
17+
"performanceConfiguration": "General web delivery",
18+
"protocol": "HTTP",
19+
"respectHeaders": true,
20+
"serveStale": true,
21+
"status": "CNAME_CONFIGURATION",
22+
"uniqueId": "354034879028850",
23+
"vendorName": "akamai"
24+
}
25+
]

0 commit comments

Comments
 (0)