@@ -49,7 +49,7 @@ type MssqlBase struct {
4949
5050 port int
5151
52- attachDatabaseUrl string
52+ usingDatabaseUrl string
5353
5454 unitTesting bool
5555
@@ -187,7 +187,7 @@ func (c *MssqlBase) AddFlags(
187187 })
188188
189189 addFlag (cmdparser.FlagOptions {
190- String : & c .attachDatabaseUrl ,
190+ String : & c .usingDatabaseUrl ,
191191 DefaultString : "" ,
192192 Name : "using" ,
193193 Usage : "Download (into container) and attach database (.bak) from URL" ,
@@ -210,7 +210,7 @@ func (c *MssqlBase) Run() {
210210 if ! c .acceptEula && viper .GetString ("ACCEPT_EULA" ) == "" {
211211 output .FatalWithHints (
212212 []string {"Either, add the --accept-eula flag to the command-line" ,
213- "Or, set the environment variable SQLCMD_ACCEPT_EULA=YES " },
213+ fmt . Sprintf ( "Or, set the environment variable i.e. %s SQLCMD_ACCEPT_EULA=YES " , pal . CreateEnvVarKeyword ()) },
214214 "EULA not accepted" )
215215 }
216216
@@ -247,20 +247,15 @@ func (c *MssqlBase) createContainer(imageName string, contextName string) {
247247 c .port = config .FindFreePortForTds ()
248248 }
249249
250- if c .attachDatabaseUrl != "" {
250+ // Do an early exit if url doesn't exist
251+ if c .usingDatabaseUrl != "" {
252+ c .validateUsingUrlExists ()
253+ }
251254
252- // At the moment we only support attaching .bak files, but we should
253- // support .bacpacs and .mdfs in the future
254- if _ , file := filepath .Split (c .attachDatabaseUrl ); filepath .Ext (file ) != ".bak" {
255- output .FatalfWithHints (
256- []string {
257- "File must be a .bak file" ,
258- },
259- "Invalid file type" )
255+ if c .defaultDatabase != "" {
256+ if ! c .validateDbName (c .defaultDatabase ) {
257+ output .Fatalf ("--user-database %q contains non-ASCII chars and/or quotes" , c .defaultDatabase )
260258 }
261-
262- // Verify the url actually exists, and early exit if it doesn't
263- urlExists (c .attachDatabaseUrl , output )
264259 }
265260
266261 controller := container .NewController ()
@@ -320,24 +315,22 @@ func (c *MssqlBase) createContainer(imageName string, contextName string) {
320315 if c .errorLogEntryToWaitFor == "Hello from Docker!" {
321316 c .sql = sql .New (sql.SqlOptions {UnitTesting : true })
322317 } else {
323- c .sql = sql .New (sql.SqlOptions {})
318+ c .sql = sql .New (sql.SqlOptions {UnitTesting : false })
324319 }
325320
326- c .sql .Connect (
327- endpoint ,
328- & sqlconfig.User {
329- AuthenticationType : "basic" ,
330- BasicAuth : & sqlconfig.BasicAuthDetails {
331- Username : "sa" ,
332- PasswordEncrypted : c .encryptPassword ,
333- Password : secret .Encode (saPassword , c .encryptPassword ),
334- },
335- Name : "sa" ,
336- }, sql.ConnectOptions {Interactive : false })
321+ saUser := & sqlconfig.User {
322+ AuthenticationType : "basic" ,
323+ BasicAuth : & sqlconfig.BasicAuthDetails {
324+ Username : "sa" ,
325+ PasswordEncrypted : c .encryptPassword ,
326+ Password : secret .Encode (saPassword , c .encryptPassword )},
327+ Name : "sa" }
328+
329+ c .sql .Connect (endpoint , saUser , sql.ConnectOptions {Interactive : false })
337330
338331 // Download and restore DB if asked
339332 defaultDbAlreadyCreated := false
340- if c .attachDatabaseUrl != "" {
333+ if c .usingDatabaseUrl != "" {
341334 defaultDbAlreadyCreated = c .downloadAndRestoreDb (
342335 controller ,
343336 containerId ,
@@ -371,6 +364,33 @@ func (c *MssqlBase) createContainer(imageName string, contextName string) {
371364 )
372365}
373366
367+ func (c * MssqlBase ) validateUsingUrlExists () {
368+ output := c .Cmd .Output ()
369+ u , err := url .Parse (c .usingDatabaseUrl )
370+ c .CheckErr (err )
371+
372+ if u .Scheme != "http" && u .Scheme != "https" {
373+ output .FatalfWithHints (
374+ []string {
375+ "--using URL must be http or https" ,
376+ },
377+ "%q is not a valid URL for --using flag" , c .usingDatabaseUrl )
378+ }
379+
380+ // At the moment we only support attaching .bak files, but we should
381+ // support .bacpacs and .mdfs in the future
382+ if _ , file := filepath .Split (c .usingDatabaseUrl ); filepath .Ext (file ) != ".bak" {
383+ output .FatalfWithHints (
384+ []string {
385+ "--using file URL must be a .bak file" ,
386+ },
387+ "Invalid --using file type" )
388+ }
389+
390+ // Verify the url actually exists, and early exit if it doesn't
391+ urlExists (c .usingDatabaseUrl , output )
392+ }
393+
374394func (c * MssqlBase ) query (commandText string ) {
375395 c .sql .Query (commandText )
376396}
@@ -428,18 +448,20 @@ func (c *MssqlBase) downloadAndRestoreDb(
428448) (defaultDatabaseAlreadyCreated bool ) {
429449 output := c .Cmd .Output ()
430450
431- u , err := url .Parse (c .attachDatabaseUrl )
451+ u , err := url .Parse (c .usingDatabaseUrl )
432452 c .CheckErr (err )
433- _ , file := filepath .Split (c .attachDatabaseUrl )
453+ _ , file := filepath .Split (c .usingDatabaseUrl )
434454 fileNameWithNoExt := strings .TrimSuffix (file , filepath .Ext (file ))
435455
436456 // Download file from URL into container
437457 output .Infof ("Downloading %s from %s" , file , u .Hostname ())
438458
459+ temporaryFolder := "/tmp"
460+
439461 controller .DownloadFile (
440462 containerId ,
441- c .attachDatabaseUrl ,
442- "/var/opt/sql/backup" ,
463+ c .usingDatabaseUrl ,
464+ temporaryFolder ,
443465 )
444466
445467 // Restore database from file
@@ -478,15 +500,15 @@ DECLARE @fileListTable TABLE (
478500)
479501
480502INSERT INTO @fileListTable
481- EXEC('RESTORE FILELISTONLY FROM DISK = ''/var/opt/sql/backup /%s''')
482- SET @sql = 'RESTORE DATABASE [%s] FROM DISK = ''/var/opt/sql/backup /%s'' WITH '
503+ EXEC('RESTORE FILELISTONLY FROM DISK = ''%s /%s''')
504+ SET @sql = 'RESTORE DATABASE [%s] FROM DISK = ''%s /%s'' WITH '
483505SELECT @sql = @sql + char(13) + ' MOVE ''' + LogicalName + ''' TO ''/var/opt/sql/' + LogicalName + '.' + RIGHT(PhysicalName,CHARINDEX('\',PhysicalName)) + ''','
484506FROM @fileListTable
485507WHERE IsPresent = 1
486508SET @sql = SUBSTRING(@sql, 1, LEN(@sql)-1)
487509EXEC(@sql)`
488510
489- c .query (fmt .Sprintf (text , file , fileNameWithNoExt , file ))
511+ c .query (fmt .Sprintf (text , temporaryFolder , file , fileNameWithNoExt , temporaryFolder , file ))
490512
491513 if c .defaultDatabase == "" {
492514 c .defaultDatabase = fileNameWithNoExt
@@ -536,3 +558,21 @@ func (c *MssqlBase) generatePassword() (password string) {
536558
537559 return
538560}
561+
562+ // validateDbName checks if the database name is something that is likely
563+ // to work seamlessly through all tools, connection strings etc.
564+ //
565+ // TODO: Right now this is any ASCII char except for quotes,
566+ // but this needs to be opened up for Kanji characters etc. with a full test suite
567+ // to ensure the database name is valid in all the places it is passed to.
568+ func (c * MssqlBase ) validateDbName (s string ) bool {
569+ for _ , b := range []byte (s ) {
570+ if b > 127 {
571+ return false
572+ }
573+ }
574+ if strings .ContainsAny (s , "'\" `'" ) {
575+ return false
576+ }
577+ return true
578+ }
0 commit comments