Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
H
helm3
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
go
helm3
Commits
d644c220
Commit
d644c220
authored
May 05, 2016
by
Matt Butcher
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #643 from technosophos/feat/install-k8s
feat(helm, tiller): implement k8s portion of install
parents
f7272f43
6db7c39b
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
199 additions
and
46 deletions
+199
-46
status.proto
_proto/hapi/release/status.proto
+6
-3
install.go
cmd/helm/install.go
+15
-6
environment.go
cmd/tiller/environment/environment.go
+43
-7
environment_test.go
cmd/tiller/environment/environment_test.go
+17
-5
release_server.go
cmd/tiller/release_server.go
+49
-9
release_server_test.go
cmd/tiller/release_server_test.go
+2
-0
alpine-pod.yaml
docs/examples/alpine/templates/alpine-pod.yaml
+6
-2
engine.go
pkg/engine/engine.go
+7
-1
engine_test.go
pkg/engine/engine_test.go
+34
-3
helm.go
pkg/helm/helm.go
+2
-1
status.pb.go
pkg/proto/hapi/release/status.pb.go
+18
-9
No files found.
_proto/hapi/release/status.proto
View file @
d644c220
...
...
@@ -12,13 +12,16 @@ option go_package = "release";
//
message
Status
{
enum
Code
{
// Status_UNKNOWN indicates that a release is in an uncertain state.
UNKNOWN
=
0
;
// Status_DEPLOYED indicates that the release has been pushed to Kubernetes.
DEPLOYED
=
1
;
// Status_DELETED indicates that a release has been deleted from Kubermetes.
DELETED
=
2
;
// Status_SUPERSEDED indicates that this release object is outdated and a newer one exists.
SUPERSEDED
=
3
;
// Status_FAILED indicates that the release was not successfully deployed.
FAILED
=
4
;
}
Code
code
=
1
;
...
...
cmd/helm/install.go
View file @
d644c220
...
...
@@ -3,11 +3,13 @@ package main
import
(
"fmt"
"os"
"time"
"github.com/spf13/cobra"
"github.com/kubernetes/helm/pkg/helm"
"github.com/kubernetes/helm/pkg/proto/hapi/release"
"github.com/kubernetes/helm/pkg/timeconv"
)
const
installDesc
=
`
...
...
@@ -25,9 +27,14 @@ const (
// install flags & args
var
(
installArg
string
// name or relative path of the chart to install
tillerHost
string
// override TILLER_HOST envVar
verbose
bool
// enable verbose install
// installArg is the name or relative path of the chart to install
installArg
string
// tillerHost overrides TILLER_HOST envVar
tillerHost
string
// verbose enables verbose output
verbose
bool
// installDryRun performs a dry-run install
installDryRun
bool
)
var
installCmd
=
&
cobra
.
Command
{
...
...
@@ -40,7 +47,7 @@ var installCmd = &cobra.Command{
func
runInstall
(
cmd
*
cobra
.
Command
,
args
[]
string
)
error
{
setupInstallEnv
(
args
)
res
,
err
:=
helm
.
InstallRelease
(
installArg
)
res
,
err
:=
helm
.
InstallRelease
(
installArg
,
installDryRun
)
if
err
!=
nil
{
return
err
}
...
...
@@ -59,8 +66,9 @@ func printRelease(rel *release.Release) {
}
fmt
.
Printf
(
"release.name: %s
\n
"
,
rel
.
Name
)
if
verbose
{
fmt
.
Printf
(
"release.info: %s
\n
"
,
rel
.
GetInfo
())
fmt
.
Printf
(
"release.chart: %s
\n
"
,
rel
.
GetChart
())
fmt
.
Printf
(
"release.info: %s %s
\n
"
,
timeconv
.
Format
(
rel
.
Info
.
LastDeployed
,
time
.
ANSIC
),
rel
.
Info
.
Status
)
fmt
.
Printf
(
"release.chart: %s %s
\n
"
,
rel
.
Chart
.
Metadata
.
Name
,
rel
.
Chart
.
Metadata
.
Version
)
fmt
.
Printf
(
"release.manifest: %s
\n
"
,
rel
.
Manifest
)
}
}
...
...
@@ -92,6 +100,7 @@ func fatalf(format string, args ...interface{}) {
func
init
()
{
installCmd
.
Flags
()
.
StringVar
(
&
tillerHost
,
"host"
,
defaultHost
,
"address of tiller server"
)
installCmd
.
Flags
()
.
BoolVarP
(
&
verbose
,
"verbose"
,
"v"
,
false
,
"enable verbose install"
)
installCmd
.
Flags
()
.
BoolVar
(
&
installDryRun
,
"dry-run"
,
false
,
"simulate an install"
)
RootCommand
.
AddCommand
(
installCmd
)
}
cmd/tiller/environment/environment.go
View file @
d644c220
/*Package environment describes the operating environment for Tiller.
Tiller's environment encapsulates all of the service dependencies Tiller has.
These dependencies are expressed as interfaces so that alternate implementations
(mocks, etc.) can be easily generated.
*/
package
environment
import
(
"io"
"github.com/kubernetes/helm/pkg/engine"
"github.com/kubernetes/helm/pkg/kube"
"github.com/kubernetes/helm/pkg/proto/hapi/chart"
"github.com/kubernetes/helm/pkg/proto/hapi/release"
"github.com/kubernetes/helm/pkg/storage"
...
...
@@ -10,6 +19,9 @@ import (
// GoTplEngine is the name of the Go template engine, as registered in the EngineYard.
const
GoTplEngine
=
"gotpl"
// DefaultNamespace is the default namespace for Tiller.
const
DefaultNamespace
=
"helm"
// DefaultEngine points to the engine that the EngineYard should treat as the
// default. A chart that does not specify an engine may be run through the
// default engine.
...
...
@@ -52,7 +64,11 @@ func (y EngineYard) Default() Engine {
// An Engine must be capable of executing multiple concurrent requests, but
// without tainting one request's environment with data from another request.
type
Engine
interface
{
Render
(
*
chart
.
Chart
,
*
chart
.
Config
)
(
map
[
string
]
string
,
error
)
// Render renders a chart.
//
// It receives a chart, a config, and a map of overrides to the config.
// Overrides are assumed to be passed from the system, not the user.
Render
(
*
chart
.
Chart
,
*
chart
.
Config
,
map
[
string
]
interface
{})
(
map
[
string
]
string
,
error
)
}
// ReleaseStorage represents a storage engine for a Release.
...
...
@@ -106,19 +122,37 @@ type ReleaseStorage interface {
//
// A KubeClient must be concurrency safe.
type
KubeClient
interface
{
//
Install takes a map where the key is a "file name" (read: unique relational
//
id) and the value is a Kubernetes manifest containing one or more resource
//
definitions
.
//
Create creates one or more resources.
//
//
namespace must contain a valid existing namespace
.
//
// TODO: Can these be in YAML or JSON, or must they be in one particular
// format?
Install
(
manifests
map
[
string
]
string
)
error
// reader must contain a YAML stream (one or more YAML documents separated
// by "\n---\n").
//
// config is optional. If nil, the client will use its existing configuration.
// If set, the client will override its default configuration with the
// passed in one.
Create
(
namespace
string
,
reader
io
.
Reader
)
error
}
// PrintingKubeClient implements KubeClient, but simply prints the reader to
// the given output.
type
PrintingKubeClient
struct
{
Out
io
.
Writer
}
// Create prints the values of what would be created with a real KubeClient.
func
(
p
*
PrintingKubeClient
)
Create
(
ns
string
,
r
io
.
Reader
)
error
{
_
,
err
:=
io
.
Copy
(
p
.
Out
,
r
)
return
err
}
// Environment provides the context for executing a client request.
//
// All services in a context are concurrency safe.
type
Environment
struct
{
// The default namespace
Namespace
string
// EngineYard provides access to the known template engines.
EngineYard
EngineYard
// Releases stores records of releases.
...
...
@@ -136,7 +170,9 @@ func New() *Environment {
GoTplEngine
:
e
,
}
return
&
Environment
{
Namespace
:
DefaultNamespace
,
EngineYard
:
ey
,
Releases
:
storage
.
NewMemory
(),
KubeClient
:
kube
.
New
(
nil
),
//&PrintingKubeClient{Out: os.Stdout},
}
}
cmd/tiller/environment/environment_test.go
View file @
d644c220
package
environment
import
(
"bytes"
"io"
"testing"
"github.com/kubernetes/helm/pkg/proto/hapi/chart"
...
...
@@ -11,7 +13,7 @@ type mockEngine struct {
out
map
[
string
]
string
}
func
(
e
*
mockEngine
)
Render
(
chrt
*
chart
.
Chart
,
v
*
chart
.
Config
)
(
map
[
string
]
string
,
error
)
{
func
(
e
*
mockEngine
)
Render
(
chrt
*
chart
.
Chart
,
v
*
chart
.
Config
,
o
map
[
string
]
interface
{}
)
(
map
[
string
]
string
,
error
)
{
return
e
.
out
,
nil
}
...
...
@@ -48,13 +50,14 @@ func (r *mockReleaseStorage) Query(labels map[string]string) ([]*release.Release
type
mockKubeClient
struct
{
}
func
(
k
*
mockKubeClient
)
Install
(
manifests
map
[
string
]
string
)
error
{
func
(
k
*
mockKubeClient
)
Create
(
ns
string
,
r
io
.
Reader
)
error
{
return
nil
}
var
_
Engine
=
&
mockEngine
{}
var
_
ReleaseStorage
=
&
mockReleaseStorage
{}
var
_
KubeClient
=
&
mockKubeClient
{}
var
_
KubeClient
=
&
PrintingKubeClient
{}
func
TestEngine
(
t
*
testing
.
T
)
{
eng
:=
&
mockEngine
{
out
:
map
[
string
]
string
{
"albatross"
:
"test"
}}
...
...
@@ -64,7 +67,7 @@ func TestEngine(t *testing.T) {
if
engine
,
ok
:=
env
.
EngineYard
.
Get
(
"test"
);
!
ok
{
t
.
Errorf
(
"failed to get engine from EngineYard"
)
}
else
if
out
,
err
:=
engine
.
Render
(
&
chart
.
Chart
{},
&
chart
.
Config
{});
err
!=
nil
{
}
else
if
out
,
err
:=
engine
.
Render
(
&
chart
.
Chart
{},
&
chart
.
Config
{}
,
map
[
string
]
interface
{}{}
);
err
!=
nil
{
t
.
Errorf
(
"unexpected template error: %s"
,
err
)
}
else
if
out
[
"albatross"
]
!=
"test"
{
t
.
Errorf
(
"expected 'test', got %q"
,
out
[
"albatross"
])
...
...
@@ -102,9 +105,18 @@ func TestKubeClient(t *testing.T) {
env
:=
New
()
env
.
KubeClient
=
kc
manifests
:=
map
[
string
]
string
{}
manifests
:=
map
[
string
]
string
{
"foo"
:
"name: value
\n
"
,
"bar"
:
"name: value
\n
"
,
}
b
:=
bytes
.
NewBuffer
(
nil
)
for
_
,
content
:=
range
manifests
{
b
.
WriteString
(
"
\n
---
\n
"
)
b
.
WriteString
(
content
)
}
if
err
:=
env
.
KubeClient
.
Install
(
manifests
);
err
!=
nil
{
if
err
:=
env
.
KubeClient
.
Create
(
"sharry-bobbins"
,
b
);
err
!=
nil
{
t
.
Errorf
(
"Kubeclient failed: %s"
,
err
)
}
}
cmd/tiller/release_server.go
View file @
d644c220
...
...
@@ -9,6 +9,7 @@ import (
"github.com/kubernetes/helm/cmd/tiller/environment"
"github.com/kubernetes/helm/pkg/proto/hapi/release"
"github.com/kubernetes/helm/pkg/proto/hapi/services"
"github.com/kubernetes/helm/pkg/storage"
"github.com/kubernetes/helm/pkg/timeconv"
"github.com/technosophos/moniker"
ctx
"golang.org/x/net/context"
...
...
@@ -99,19 +100,44 @@ func (s *releaseServer) UpdateRelease(c ctx.Context, req *services.UpdateRelease
return
nil
,
errNotImplemented
}
func
(
s
*
releaseServer
)
uniqName
()
(
string
,
error
)
{
maxTries
:=
5
for
i
:=
0
;
i
<
maxTries
;
i
++
{
namer
:=
moniker
.
New
()
name
:=
namer
.
NameSep
(
"-"
)
if
_
,
err
:=
s
.
env
.
Releases
.
Read
(
name
);
err
==
storage
.
ErrNotFound
{
return
name
,
nil
}
log
.
Printf
(
"info: Name %q is taken. Searching again."
,
name
)
}
log
.
Printf
(
"warning: No available release names found after %d tries"
,
maxTries
)
return
"ERROR"
,
errors
.
New
(
"no available release name found"
)
}
func
(
s
*
releaseServer
)
InstallRelease
(
c
ctx
.
Context
,
req
*
services
.
InstallReleaseRequest
)
(
*
services
.
InstallReleaseResponse
,
error
)
{
if
req
.
Chart
==
nil
{
return
nil
,
errMissingChart
}
// We should probably make a name generator part of the Environment.
namer
:=
moniker
.
New
()
// TODO: Make sure this is unique.
name
:=
namer
.
NameSep
(
"-"
)
ts
:=
timeconv
.
Now
()
name
,
err
:=
s
.
uniqName
()
if
err
!=
nil
{
return
nil
,
err
}
overrides
:=
map
[
string
]
interface
{}{
"Release"
:
map
[
string
]
interface
{}{
"Name"
:
name
,
"Time"
:
ts
,
"Namespace"
:
s
.
env
.
Namespace
,
"Service"
:
"Tiller"
,
},
"Chart"
:
req
.
Chart
.
Metadata
,
}
// Render the templates
files
,
err
:=
s
.
env
.
EngineYard
.
Default
()
.
Render
(
req
.
Chart
,
req
.
Values
)
// TODO: Fix based on whether chart has `engine: SOMETHING` set.
files
,
err
:=
s
.
env
.
EngineYard
.
Default
()
.
Render
(
req
.
Chart
,
req
.
Values
,
overrides
)
if
err
!=
nil
{
return
nil
,
err
}
...
...
@@ -139,16 +165,30 @@ func (s *releaseServer) InstallRelease(c ctx.Context, req *services.InstallRelea
Manifest
:
b
.
String
(),
}
res
:=
&
services
.
InstallReleaseResponse
{
Release
:
r
}
if
req
.
DryRun
{
log
.
Printf
(
"Dry run for %s"
,
name
)
return
&
services
.
InstallReleaseResponse
{
Release
:
r
}
,
nil
return
res
,
nil
}
if
err
:=
s
.
env
.
Releases
.
Create
(
r
);
err
!=
nil
{
return
nil
,
err
if
err
:=
s
.
env
.
KubeClient
.
Create
(
s
.
env
.
Namespace
,
b
);
err
!=
nil
{
r
.
Info
.
Status
.
Code
=
release
.
Status_FAILED
log
.
Printf
(
"warning: Release %q failed: %s"
,
name
,
err
)
return
res
,
fmt
.
Errorf
(
"release %s failed: %s"
,
name
,
err
)
}
return
&
services
.
InstallReleaseResponse
{
Release
:
r
},
nil
// This is a tricky case. The release has been created, but the result
// cannot be recorded. The truest thing to tell the user is that the
// release was created. However, the user will not be able to do anything
// further with this release.
//
// One possible strategy would be to do a timed retry to see if we can get
// this stored in the future.
if
err
:=
s
.
env
.
Releases
.
Create
(
r
);
err
!=
nil
{
log
.
Printf
(
"warning: Failed to record release %q: %s"
,
name
,
err
)
}
return
res
,
nil
}
func
(
s
*
releaseServer
)
UninstallRelease
(
c
ctx
.
Context
,
req
*
services
.
UninstallReleaseRequest
)
(
*
services
.
UninstallReleaseResponse
,
error
)
{
...
...
cmd/tiller/release_server_test.go
View file @
d644c220
...
...
@@ -2,6 +2,7 @@ package main
import
(
"fmt"
"os"
"strings"
"testing"
...
...
@@ -219,6 +220,7 @@ func TestListReleases(t *testing.T) {
func
mockEnvironment
()
*
environment
.
Environment
{
e
:=
environment
.
New
()
e
.
Releases
=
storage
.
NewMemory
()
e
.
KubeClient
=
&
environment
.
PrintingKubeClient
{
Out
:
os
.
Stdout
}
return
e
}
...
...
docs/examples/alpine/templates/alpine-pod.yaml
View file @
d644c220
apiVersion
:
v1
kind
:
Pod
metadata
:
name
:
{{
default "alpine" .n
ame
}}
name
:
{{
.Release.Name
}}
-{{.Chart.N
ame}}
labels
:
heritage
:
helm
heritage
:
{{
.Release.Service
}}
chartName
:
{{
.Chart.Name
}}
chartVersion
:
{{
.Chart.Version
}}
annotations
:
"
helm.sh/created"
:
{{
.Release.Time.Seconds
}}
spec
:
restartPolicy
:
{{
default "Never" .restart_policy
}}
containers
:
...
...
pkg/engine/engine.go
View file @
d644c220
...
...
@@ -59,7 +59,7 @@ func New() *Engine {
// - Scalar values and arrays are replaced, maps are merged
// - A chart has access to all of the variables for it, as well as all of
// the values destined for its dependencies.
func
(
e
*
Engine
)
Render
(
chrt
*
chart
.
Chart
,
vals
*
chart
.
Config
)
(
map
[
string
]
string
,
error
)
{
func
(
e
*
Engine
)
Render
(
chrt
*
chart
.
Chart
,
vals
*
chart
.
Config
,
overrides
map
[
string
]
interface
{}
)
(
map
[
string
]
string
,
error
)
{
var
cvals
chartutil
.
Values
// Parse values if not nil. We merge these at the top level because
...
...
@@ -69,6 +69,12 @@ func (e *Engine) Render(chrt *chart.Chart, vals *chart.Config) (map[string]strin
if
err
!=
nil
{
return
map
[
string
]
string
{},
err
}
// Override the top-level values. Overrides are NEVER merged deeply.
// The assumption is that an override is intended to set an explicit
// and exact value.
for
k
,
v
:=
range
overrides
{
evals
[
k
]
=
v
}
cvals
=
coalesceValues
(
chrt
,
evals
)
}
...
...
pkg/engine/engine_test.go
View file @
d644c220
...
...
@@ -22,7 +22,38 @@ func TestEngine(t *testing.T) {
}
func
TestRender
(
t
*
testing
.
T
)
{
t
.
Skip
()
c
:=
&
chart
.
Chart
{
Metadata
:
&
chart
.
Metadata
{
Name
:
"moby"
,
Version
:
"1.2.3"
,
},
Templates
:
[]
*
chart
.
Template
{
{
Name
:
"test1"
,
Data
:
[]
byte
(
"{{.outer | title }} {{.inner | title}}"
)},
},
Values
:
&
chart
.
Config
{
Raw
:
`outer = "DEFAULT"\ninner= "DEFAULT"\n`
,
},
}
vals
:=
&
chart
.
Config
{
Raw
:
`outer = "BAD"
inner= "inn"`
,
}
overrides
:=
map
[
string
]
interface
{}{
"outer"
:
"spouter"
,
}
e
:=
New
()
out
,
err
:=
e
.
Render
(
c
,
vals
,
overrides
)
if
err
!=
nil
{
t
.
Errorf
(
"Failed to render templates: %s"
,
err
)
}
expect
:=
"Spouter Inn"
if
out
[
"test1"
]
!=
expect
{
t
.
Errorf
(
"Expected %q, got %q"
,
expect
,
out
[
"test1"
])
}
}
func
TestRenderInternals
(
t
*
testing
.
T
)
{
...
...
@@ -129,7 +160,7 @@ func TestRenderDependency(t *testing.T) {
},
}
out
,
err
:=
e
.
Render
(
ch
,
nil
)
out
,
err
:=
e
.
Render
(
ch
,
nil
,
map
[
string
]
interface
{}{}
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to render chart: %s"
,
err
)
...
...
@@ -190,7 +221,7 @@ func TestRenderNestedValues(t *testing.T) {
what = "flower"`
,
}
out
,
err
:=
e
.
Render
(
outer
,
&
inject
)
out
,
err
:=
e
.
Render
(
outer
,
&
inject
,
map
[
string
]
interface
{}{}
)
if
err
!=
nil
{
t
.
Fatalf
(
"failed to render templates: %s"
,
err
)
}
...
...
pkg/helm/helm.go
View file @
d644c220
...
...
@@ -71,7 +71,7 @@ func UninstallRelease(name string) (*services.UninstallReleaseResponse, error) {
}
// InstallRelease installs a new chart and returns the release response.
func
InstallRelease
(
chStr
string
)
(
*
services
.
InstallReleaseResponse
,
error
)
{
func
InstallRelease
(
chStr
string
,
dryRun
bool
)
(
*
services
.
InstallReleaseResponse
,
error
)
{
chfi
,
err
:=
chartutil
.
LoadChart
(
chStr
)
if
err
!=
nil
{
return
nil
,
err
...
...
@@ -90,5 +90,6 @@ func InstallRelease(chStr string) (*services.InstallReleaseResponse, error) {
return
Config
.
client
()
.
install
(
&
services
.
InstallReleaseRequest
{
Chart
:
chpb
,
Values
:
vals
,
DryRun
:
dryRun
,
})
}
pkg/proto/hapi/release/status.pb.go
View file @
d644c220
...
...
@@ -17,10 +17,16 @@ var _ = math.Inf
type
Status_Code
int32
const
(
Status_UNKNOWN
Status_Code
=
0
Status_DEPLOYED
Status_Code
=
1
Status_DELETED
Status_Code
=
2
// Status_UNKNOWN indicates that a release is in an uncertain state.
Status_UNKNOWN
Status_Code
=
0
// Status_DEPLOYED indicates that the release has been pushed to Kubernetes.
Status_DEPLOYED
Status_Code
=
1
// Status_DELETED indicates that a release has been deleted from Kubermetes.
Status_DELETED
Status_Code
=
2
// Status_SUPERSEDED indicates that this release object is outdated and a newer one exists.
Status_SUPERSEDED
Status_Code
=
3
// Status_FAILED indicates that the release was not successfully deployed.
Status_FAILED
Status_Code
=
4
)
var
Status_Code_name
=
map
[
int32
]
string
{
...
...
@@ -28,12 +34,14 @@ var Status_Code_name = map[int32]string{
1
:
"DEPLOYED"
,
2
:
"DELETED"
,
3
:
"SUPERSEDED"
,
4
:
"FAILED"
,
}
var
Status_Code_value
=
map
[
string
]
int32
{
"UNKNOWN"
:
0
,
"DEPLOYED"
:
1
,
"DELETED"
:
2
,
"SUPERSEDED"
:
3
,
"FAILED"
:
4
,
}
func
(
x
Status_Code
)
String
()
string
{
...
...
@@ -68,19 +76,20 @@ func init() {
}
var
fileDescriptor2
=
[]
byte
{
// 2
15
bytes of a gzipped FileDescriptorProto
// 2
26
bytes of a gzipped FileDescriptorProto
0x1f
,
0x8b
,
0x08
,
0x00
,
0x00
,
0x09
,
0x6e
,
0x88
,
0x02
,
0xff
,
0xe2
,
0x92
,
0xcc
,
0x48
,
0x2c
,
0xc8
,
0xd4
,
0x2f
,
0x4a
,
0xcd
,
0x49
,
0x4d
,
0x2c
,
0x4e
,
0xd5
,
0x2f
,
0x2e
,
0x49
,
0x2c
,
0x29
,
0x2d
,
0xd6
,
0x2b
,
0x28
,
0xca
,
0x2f
,
0xc9
,
0x17
,
0xe2
,
0x01
,
0x49
,
0xe9
,
0x41
,
0xa5
,
0xa4
,
0x24
,
0xd3
,
0xf3
,
0xf3
,
0xd3
,
0x73
,
0x52
,
0xf5
,
0xc1
,
0x72
,
0x49
,
0xa5
,
0x69
,
0xfa
,
0x89
,
0x79
,
0x95
,
0x10
,
0x85
,
0x4a
,
0x
c
b
,
0x19
,
0xb9
,
0xd8
,
0x82
,
0xc1
,
0x3a
,
0x85
,
0x74
,
0xb9
,
0x58
,
0x92
,
0xf3
,
0x53
,
0x52
,
0x4a
,
0x
9
b
,
0x19
,
0xb9
,
0xd8
,
0x82
,
0xc1
,
0x3a
,
0x85
,
0x74
,
0xb9
,
0x58
,
0x92
,
0xf3
,
0x53
,
0x52
,
0x25
,
0x18
,
0x15
,
0x18
,
0x35
,
0xf8
,
0x8c
,
0x24
,
0xf5
,
0x90
,
0x8d
,
0xd0
,
0x83
,
0xa8
,
0xd1
,
0x73
,
0x06
,
0x2a
,
0x08
,
0x02
,
0x2b
,
0x13
,
0xd2
,
0xe3
,
0x62
,
0x4f
,
0x49
,
0x2d
,
0x49
,
0xcc
,
0xcc
,
0x29
,
0x96
,
0x60
,
0x02
,
0xea
,
0xe0
,
0x36
,
0x12
,
0xd1
,
0x83
,
0x58
,
0xa3
,
0x07
,
0xb3
,
0x46
,
0xcf
,
0x31
,
0xaf
,
0x32
,
0x08
,
0xa6
,
0x48
,
0xc9
,
0x8
e
,
0x8b
,
0x05
,
0xa4
,
0x5b
,
0x88
,
0x9b
,
0x8b
,
0x3d
,
0xd4
,
0xaf
,
0x32
,
0x08
,
0xa6
,
0x48
,
0xc9
,
0x8
b
,
0x8b
,
0x05
,
0xa4
,
0x5b
,
0x88
,
0x9b
,
0x8b
,
0x3d
,
0xd4
,
0xcf
,
0xdb
,
0xcf
,
0x3f
,
0xdc
,
0x4f
,
0x80
,
0x41
,
0x88
,
0x87
,
0x8b
,
0xc3
,
0xc5
,
0x35
,
0xc0
,
0xc7
,
0x3f
,
0xd2
,
0xd5
,
0x45
,
0x80
,
0x11
,
0x24
,
0xe5
,
0xe2
,
0xea
,
0xe3
,
0x1a
,
0x02
,
0xe4
,
0x30
,
0x09
,
0xf1
,
0x71
,
0x71
,
0x05
,
0x87
,
0x06
,
0xb8
,
0x06
,
0x05
,
0xbb
,
0xba
,
0x00
,
0xf9
,
0xcc
,
0x4e
,
0x9c
,
0x51
,
0xec
,
0x50
,
0xc7
,
0x24
,
0xb1
,
0x81
,
0x6d
,
0x30
,
0x06
,
0x04
,
0x00
,
0x00
,
0xff
,
0xff
,
0x0d
,
0xcd
,
0xe7
,
0x6f
,
0x01
,
0x01
,
0x00
,
0x00
,
0xf1
,
0x71
,
0x71
,
0x05
,
0x87
,
0x06
,
0xb8
,
0x06
,
0x05
,
0xbb
,
0xba
,
0x00
,
0xf9
,
0xcc
,
0x42
,
0x5c
,
0x5c
,
0x6c
,
0x6e
,
0x8e
,
0x9e
,
0x3e
,
0x40
,
0x36
,
0x8b
,
0x13
,
0x67
,
0x14
,
0x3b
,
0xd4
,
0x61
,
0x49
,
0x6c
,
0x60
,
0xdb
,
0x8c
,
0x01
,
0x01
,
0x00
,
0x00
,
0xff
,
0xff
,
0x8c
,
0x99
,
0x9a
,
0x3b
,
0x0d
,
0x01
,
0x00
,
0x00
,
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment