~cloudbaseit/charms/win2012r2/drupal-iis/trunk

« back to all changes in this revision

Viewing changes to lib/Modules/JujuUtils/JujuUtils.psm1

  • Committer: Ionut Balutoiu
  • Date: 2016-12-23 12:55:39 UTC
  • Revision ID: ibalutoiu@cloudbasesolutions.com-20161223125539-j795kbynab3uflha
Added charm code

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright 2014-2015 Cloudbase Solutions Srl
 
2
#
 
3
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 
4
#    not use this file except in compliance with the License. You may obtain
 
5
#    a copy of the License at
 
6
#
 
7
#         http://www.apache.org/licenses/LICENSE-2.0
 
8
#
 
9
#    Unless required by applicable law or agreed to in writing, software
 
10
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 
11
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 
12
#    License for the specific language governing permissions and limitations
 
13
#    under the License.
 
14
 
 
15
Import-Module JujuLogging
 
16
 
 
17
function Convert-FileToBase64{
 
18
    <#
 
19
    .SYNOPSIS
 
20
    This powershell commandlet converts an entire file, byte by byte to base64 and returns the string.
 
21
 
 
22
    WARNING: Do not use this to convert large files, as it reads the entire contents of a file
 
23
    into memory. This function may be useful to transfer small amounts of data over a relation
 
24
    without having to worry about encoding or escaping, preserving at the same time any
 
25
    binary info/special
 
26
    characters.
 
27
    .PARAMETER File
 
28
    The path to the file you want to convert. It works for any type of file. Take great care not to
 
29
    try and convert large files.
 
30
    #>
 
31
    [CmdletBinding()]
 
32
    Param (
 
33
        [parameter(Mandatory=$true)]
 
34
        [string]$File,
 
35
        [switch]$Force
 
36
    )
 
37
    PROCESS {
 
38
        if(!(Test-Path $File)) {
 
39
            Throw "No such file: $File"
 
40
        }
 
41
        $f = (Get-Item $File)
 
42
        if($f.Length -gt 1MB -and !$Force) {
 
43
            Throw "File is too big to convert (> 1MB). Use -Force to do it anyway..."
 
44
        }
 
45
        $ct = [System.IO.File]::ReadAllBytes($File)
 
46
        $b64 = [Convert]::ToBase64String($ct)
 
47
        return $b64
 
48
    }
 
49
}
 
50
 
 
51
function Write-FileFromBase64 {
 
52
    <#
 
53
    .SYNOPSIS
 
54
    Helper function that converts base64 to bytes and then writes that stream to a file.
 
55
    .PARAMETER File
 
56
    Destination file to write to.
 
57
    .PARAMETER Content
 
58
    Base64 encoded string
 
59
    #>
 
60
    [CmdletBinding()]
 
61
    Param (
 
62
        [Parameter(Mandatory=$true)]
 
63
        [string]$File,
 
64
        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
 
65
        [string]$Content
 
66
    )
 
67
    PROCESS {
 
68
        $bytes = [Convert]::FromBase64String($Content)
 
69
        [System.IO.File]::WriteAllBytes($File, $bytes)
 
70
    }
 
71
}
 
72
 
 
73
function ConvertTo-Base64 {
 
74
    <#
 
75
    .SYNOPSIS
 
76
    Convert string to its base64 representation
 
77
    .PARAMETER Content
 
78
    String to be converted to base64
 
79
    #>
 
80
    [CmdletBinding()]
 
81
    Param (
 
82
        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
 
83
        [string]$Content
 
84
    )
 
85
    PROCESS {
 
86
        $x = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($Content))
 
87
        return $x
 
88
    }
 
89
}
 
90
 
 
91
function ConvertFrom-Base64 {
 
92
    <#
 
93
    .SYNOPSIS
 
94
    Convert base64 back to string
 
95
    .PARAMETER Content
 
96
    Base64 encoded string
 
97
    #>
 
98
    [CmdletBinding()]
 
99
    Param (
 
100
        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
 
101
        [string]$Content
 
102
    )
 
103
    PROCESS {
 
104
        $x = [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($content))
 
105
        return $x
 
106
    }
 
107
}
 
108
 
 
109
function Get-EncryptedString {
 
110
    <#
 
111
    .SYNOPSIS
 
112
    This is just a helper function that converts a plain string to a secure string and returns the encrypted
 
113
    string representation.
 
114
    .PARAMETER Content
 
115
    The string you want to encrypt
 
116
    #>
 
117
    [CmdletBinding()]
 
118
    Param (
 
119
        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
 
120
        [string]$Content
 
121
    )
 
122
    PROCESS {
 
123
        $ret = ConvertTo-SecureString -AsPlainText -Force $Content | ConvertFrom-SecureString
 
124
        return $ret
 
125
    }
 
126
}
 
127
 
 
128
function Get-DecryptedString {
 
129
    <#
 
130
    .SYNOPSIS
 
131
    Decrypt a securestring back to its plain text representation.
 
132
    .PARAMETER Content
 
133
    The encrypted content to decrypt.
 
134
    .NOTES
 
135
    This function is only meant to be used with encrypted strings, not binary.
 
136
    #>
 
137
    [CmdletBinding()]
 
138
    Param (
 
139
        [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
 
140
        [string]$Content
 
141
    )
 
142
    PROCESS {
 
143
        $c = ConvertTo-SecureString $Content
 
144
        $dec = [System.Runtime.InteropServices.Marshal]::SecureStringToCoTaskMemUnicode($c)
 
145
        $ret = [System.Runtime.InteropServices.Marshal]::PtrToStringUni($dec)
 
146
        [System.Runtime.InteropServices.Marshal]::ZeroFreeCoTaskMemUnicode($dec)
 
147
        return $ret
 
148
    }
 
149
}
 
150
 
 
151
function Get-UserPath {
 
152
    <#
 
153
    .SYNOPSIS
 
154
    Returns the $env:PATH variable for the current user.
 
155
    #>
 
156
    PROCESS {
 
157
        return [System.Environment]::GetEnvironmentVariable("PATH", "User")
 
158
    }
 
159
}
 
160
 
 
161
function Get-SystemPath {
 
162
    <#
 
163
    .SYNOPSIS
 
164
    Returns the system wide default $env:PATH.
 
165
    #>
 
166
    PROCESS {
 
167
        return [System.Environment]::GetEnvironmentVariable("PATH", "Machine")
 
168
    }
 
169
}
 
170
 
 
171
function Compare-ScriptBlocks {
 
172
    <#
 
173
    .SYNOPSIS
 
174
    Compare two script blocks
 
175
    .PARAMETER ScriptBlock1
 
176
    First script block
 
177
    .PARAMETER ScriptBlock2
 
178
    Second script block
 
179
    #>
 
180
    [CmdletBinding()]
 
181
    Param(
 
182
        [Parameter(Mandatory=$true)]
 
183
        [Alias("scrBlock1")]
 
184
        [System.Management.Automation.ScriptBlock]$ScriptBlock1,
 
185
        [Parameter(Mandatory=$true)]
 
186
        [Alias("scrBlock2")]
 
187
        [System.Management.Automation.ScriptBlock]$ScriptBlock2
 
188
    )
 
189
    PROCESS {
 
190
        $sb1 = $ScriptBlock1.ToString()
 
191
        $sb2 = $ScriptBlock2.ToString()
 
192
        return ($sb1.CompareTo($sb2) -eq 0)
 
193
    }
 
194
}
 
195
 
 
196
function Compare-Arrays {
 
197
    <#
 
198
    .SYNOPSIS
 
199
    Compare two arrays. Returns a boolean value that determines whether or not the arrays are equal.
 
200
    .PARAMETER Array1
 
201
    First array to compare
 
202
    .PARAMETER Array2
 
203
    Second array to compare
 
204
    #>
 
205
    [CmdletBinding()]
 
206
    Param(
 
207
        [Parameter(Mandatory=$true)]
 
208
        [Alias("arr1")]
 
209
        [System.Object[]]$Array1,
 
210
        [Parameter(Mandatory=$true)]
 
211
        [Alias("arr2")]
 
212
        [System.Object[]]$Array2
 
213
    )
 
214
    PROCESS {
 
215
        return (((Compare-Object $Array1 $Array2).InputObject).Length -eq 0)
 
216
    }
 
217
}
 
218
 
 
219
function Compare-HashTables {
 
220
    <#
 
221
    .SYNOPSIS
 
222
    Compare two arrays. Returns a boolean value that determines whether or not the arrays are equal. This function only works for flat hashtables.
 
223
    .PARAMETER Array1
 
224
    First array to compare
 
225
    .PARAMETER Array2
 
226
    Second array to compare
 
227
    #>
 
228
    [CmdletBinding()]
 
229
    Param(
 
230
        [Parameter(Mandatory=$true)]
 
231
        [Alias("tab1")]
 
232
        [HashTable]$HashTable1,
 
233
        [Parameter(Mandatory=$true)]
 
234
        [Alias("tab2")]
 
235
        [HashTable]$HashTable2
 
236
    )
 
237
    PROCESS {
 
238
        if ($HashTable1.Count -ne $HashTable2.Count) {
 
239
            return $false
 
240
        }
 
241
        foreach ($i in $HashTable1.Keys) {
 
242
            if (($HashTable2.ContainsKey($i) -eq $false) -or ($HashTable1[$i] -ne $HashTable2[$i])) {
 
243
                return $false
 
244
            }
 
245
        }
 
246
        return $true
 
247
    }
 
248
}
 
249
 
 
250
function Start-ExternalCommand {
 
251
    <#
 
252
    .SYNOPSIS
 
253
    Helper function to execute a script block and throw an exception in case of error.
 
254
    .PARAMETER ScriptBlock
 
255
    Script block to execute
 
256
    .PARAMETER ArgumentList
 
257
    A list of parameters to pass to Invoke-Command
 
258
    .PARAMETER ErrorMessage
 
259
    Optional error message. This will become part of the exception message we throw in case of an error.
 
260
    #>
 
261
    [CmdletBinding()]
 
262
    param(
 
263
        [Parameter(Mandatory=$true)]
 
264
        [Alias("Command")]
 
265
        [ScriptBlock]$ScriptBlock,
 
266
        [array]$ArgumentList=@(),
 
267
        [string]$ErrorMessage
 
268
    )
 
269
    PROCESS {
 
270
        if($LASTEXITCODE){
 
271
            # Leftover exit code. Some other process failed, and this
 
272
            # function was called before it was resolved.
 
273
            # There is no way to determine if the ScriptBlock contains
 
274
            # a powershell commandlet or a native application. So we clear out
 
275
            # the LASTEXITCODE variable before we execute. By this time, the value of
 
276
            # the variable is not to be trusted for error detection anyway.
 
277
            $LASTEXITCODE = ""
 
278
        }
 
279
        $res = Invoke-Command -ScriptBlock $ScriptBlock -ArgumentList $ArgumentList
 
280
        if ($LASTEXITCODE) {
 
281
            if(!$ErrorMessage){
 
282
                Throw ("Command exited with status: {0}" -f $LASTEXITCODE)
 
283
            }
 
284
            throw ("{0} (Exit code: $LASTEXITCODE)" -f $ErrorMessage)
 
285
        }
 
286
        return $res
 
287
    }
 
288
}
 
289
 
 
290
function Start-ExecuteWithRetry {
 
291
    <#
 
292
    .SYNOPSIS
 
293
    In some cases a command may fail several times before it succeeds, be it because of network outage, or a service
 
294
    not being ready yet, etc. This is a helper function to allow you to execute a function or binary a number of times
 
295
    before actually failing.
 
296
 
 
297
    Its important to note, that any powershell commandlet or native command can be executed using this function. The result
 
298
    of that command or powershell commandlet will be returned by this function.
 
299
 
 
300
    Only the last exception will be thrown, and will be logged with a log level of ERROR.
 
301
    .PARAMETER ScriptBlock
 
302
    The script block to run.
 
303
    .PARAMETER MaxRetryCount
 
304
    The number of retries before we throw an exception.
 
305
    .PARAMETER RetryInterval
 
306
    Number of seconds to sleep between retries.
 
307
    .PARAMETER ArgumentList
 
308
    Arguments to pass to your wrapped commandlet/command.
 
309
 
 
310
    .EXAMPLE
 
311
    # If the computer just booted after the machine just joined the domain, and your charm starts running,
 
312
    # it may error out until the security policy has been fully applied. In the bellow example we retry 10
 
313
    # times and wait 10 seconds between retries before we give up. If successful, $ret will contain the result
 
314
    # of Get-ADUser. If it does not, an exception is thrown. 
 
315
    $ret = Start-ExecuteWithRetry -ScriptBlock {
 
316
        Get-ADUser testuser
 
317
    } -MaxRetryCount 10 -RetryInterval 10
 
318
    #>
 
319
    [CmdletBinding()]
 
320
    param(
 
321
        [Parameter(Mandatory=$true)]
 
322
        [Alias("Command")]
 
323
        [ScriptBlock]$ScriptBlock,
 
324
        [int]$MaxRetryCount=10,
 
325
        [int]$RetryInterval=3,
 
326
        [array]$ArgumentList=@()
 
327
    )
 
328
    PROCESS {
 
329
        $currentErrorActionPreference = $ErrorActionPreference
 
330
        $ErrorActionPreference = "Continue"
 
331
 
 
332
        $retryCount = 0
 
333
        while ($true) {
 
334
            try {
 
335
                $res = Invoke-Command -ScriptBlock $ScriptBlock `
 
336
                         -ArgumentList $ArgumentList
 
337
                $ErrorActionPreference = $currentErrorActionPreference
 
338
                return $res
 
339
            } catch [System.Exception] {
 
340
                $retryCount++
 
341
                if ($retryCount -gt $MaxRetryCount) {
 
342
                    $ErrorActionPreference = $currentErrorActionPreference
 
343
                    throw
 
344
                } else {
 
345
                    if($_) {
 
346
                        Write-HookTracebackToLog $_ -LogLevel WARNING
 
347
                    }
 
348
                    Start-Sleep $RetryInterval
 
349
                }
 
350
            }
 
351
        }
 
352
    }
 
353
}
 
354
 
 
355
function Get-SanePath {
 
356
    <#
 
357
    .SYNOPSIS
 
358
    There are some situations in which the $env:PATH variable may contain duplicate paths. This function returns
 
359
    a sanitized $env:PATH without any duplicates.
 
360
    #>
 
361
    PROCESS {
 
362
        $path = $env:PATH
 
363
        $arrayPath = $path.Split(';')
 
364
        $arrayPath = $arrayPath | Select-Object -Unique
 
365
        $newPath = $arrayPath -join ';'
 
366
        return $newPath
 
367
    }
 
368
}
 
369
 
 
370
function Add-ToUserPath {
 
371
    <#
 
372
    .SYNOPSIS
 
373
    Permanently add an additional path to $env:PATH for current user, and also set the current $env:PATH to the new value.
 
374
    .PARAMETER Path
 
375
    Extra path to add to $env:PATH
 
376
    #>
 
377
    [CmdletBinding()]
 
378
    Param(
 
379
        [Parameter(Mandatory=$true)]
 
380
        [string]$Path
 
381
    )
 
382
    PROCESS {
 
383
        $currentPath = Get-SanePath
 
384
        if ($Path -in $env:Path.Split(';')){
 
385
            return
 
386
        }
 
387
        $newPath = $currentPath + ";" + $Path
 
388
        Start-ExternalCommand -Command {
 
389
            setx PATH $newPath
 
390
        } -ErrorMessage "Failed to set user path"
 
391
        $env:PATH = $newPath
 
392
    }
 
393
}
 
394
 
 
395
function Get-MarshaledObject {
 
396
    <#
 
397
    .SYNOPSIS
 
398
    Get a base64 encoded representation of a yaml encoded powershell object. "Why?" you might ask. Well, in some cases you
 
399
    may need to send more complex information through a relation to another charm. This function allows you to send simple
 
400
    powershell objects (hashtables, arrays, etc) as base64 encoded strings. This function first encodes them to yaml, and
 
401
    then to base64 encoded strings.
 
402
 
 
403
    This also allows us to send the same information to any kind of charm that can unmarshal yaml to a native type (say python).
 
404
    .PARAMETER Object
 
405
 
 
406
    .NOTES
 
407
    ConvertTo-Base64 uses utf-16-le encoding for objects
 
408
 
 
409
    .EXAMPLE
 
410
 
 
411
    $obj = @{"Hello"="world";}
 
412
    Get-MarshaledObject -Object $obj
 
413
    ewANAAoAIAAgACAAIAAiAEgAZQBsAGwAbwAiADoAIAAgACIAdwBvAHIAbABkACIADQAKAH0A
 
414
    #>
 
415
    [CmdletBinding()]
 
416
    Param(
 
417
        [Parameter(Mandatory=$true)]
 
418
        [Alias("obj")]
 
419
        $Object
 
420
    )
 
421
    PROCESS {
 
422
        $encoded = $Object | ConvertTo-Yaml
 
423
        $b64 = ConvertTo-Base64 $encoded
 
424
        return $b64
 
425
    }
 
426
}
 
427
 
 
428
function Get-UnmarshaledObject {
 
429
    <#
 
430
    .SYNOPSIS
 
431
    Try to convert a base64 encoded string back to a powershell object.
 
432
    .PARAMETER Object
 
433
    The base64 encoded representation of the object we want to unmarshal. 
 
434
    #>
 
435
    [CmdletBinding()]
 
436
    Param(
 
437
        [Parameter(Mandatory=$true)]
 
438
        [Alias("obj")]
 
439
        [string]$Object
 
440
    )
 
441
    PROCESS {
 
442
        $decode = ConvertFrom-Base64 $Object
 
443
        $ret = $decode | ConvertFrom-Yaml
 
444
        return $ret
 
445
    }
 
446
}
 
447
 
 
448
function Get-CmdStringFromHashtable {
 
449
    <#
 
450
    .SYNOPSIS
 
451
    Convert a hashtable to a command line key/value string. Values for hashtable keys must be string or int. The result is usually suitable for native commands executed via cmd.exe.
 
452
    .PARAMETER Parameters
 
453
    hashtable containing command line parameters.
 
454
 
 
455
    .EXAMPLE
 
456
    $params = @{
 
457
        "firstname"="John";
 
458
        "lastname"="Doe";
 
459
        "age"="20";
 
460
    }
 
461
    Get-CmdStringFromHashtable $params
 
462
    age=20 firstname=John lastname=Doe
 
463
    #>
 
464
    [CmdletBinding()]
 
465
    param(
 
466
        [Parameter(Mandatory=$true)]
 
467
        [Alias("params")]
 
468
        [Hashtable]$Parameters
 
469
    )
 
470
    PROCESS {
 
471
        $args = ""
 
472
        foreach($i in $Parameters.GetEnumerator()) {
 
473
            $args += $i.key + "=" + $i.value + " "
 
474
        }
 
475
        return $args.Trim()
 
476
    }
 
477
}
 
478
 
 
479
function Get-EscapedQuotedString {
 
480
    [CmdletBinding()]
 
481
    param(
 
482
        [string]$value
 
483
    )
 
484
    PROCESS {
 
485
        return "'" + $value.Replace("'", "''") + "'"
 
486
    }
 
487
}
 
488
 
 
489
function Get-PSStringParamsFromHashtable {
 
490
    <#
 
491
    .SYNOPSIS
 
492
    Convert a hashtable to a powershell command line options. Values can be any powershell objects.
 
493
    .PARAMETER Parameters
 
494
    hashtable containing command line parameters.
 
495
 
 
496
    .EXAMPLE
 
497
    $params = @{
 
498
        "firstname"="John";
 
499
        "lastname"="Doe";
 
500
        "age"="20";
 
501
    }
 
502
    Get-PSStringParamsFromHashtable $params
 
503
    -age 20 -firstname John -lastname Doe
 
504
    #>
 
505
    [CmdletBinding()]
 
506
    param(
 
507
        [Parameter(Mandatory=$true)]
 
508
        [Hashtable]$params
 
509
    )
 
510
    PROCESS {
 
511
        $args = @()
 
512
        foreach($i in $params.GetEnumerator()) {
 
513
            $args += @(("-" + $i.key), $i.value)
 
514
        }
 
515
 
 
516
        return $args -join " "
 
517
    }
 
518
}
 
519
 
 
520
Export-ModuleMember -Function * -Alias *